Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormeta-androcto <meta.androcto1@gmail.com>2016-04-28 05:36:09 +0300
committermeta-androcto <meta.androcto1@gmail.com>2016-04-28 05:36:09 +0300
commitc3a6d9132a44afd254fa120157c66e72b751e6f1 (patch)
treeace2279cc062a97a8f90bfd7aed27e21b10626bd /add_curve_sapling/utils.py
parentfe34f82e7059bb6b89dfc88b55f030111f2d431f (diff)
Update sapling tree gen: T46559 major rewrite
Diffstat (limited to 'add_curve_sapling/utils.py')
-rw-r--r--add_curve_sapling/utils.py1774
1 files changed, 1325 insertions, 449 deletions
diff --git a/add_curve_sapling/utils.py b/add_curve_sapling/utils.py
index c07bbf9d..55c0f14f 100644
--- a/add_curve_sapling/utils.py
+++ b/add_curve_sapling/utils.py
@@ -16,30 +16,35 @@
#
# ##### END GPL LICENSE BLOCK #####
+print("version 3 imported")
import bpy
import time
import copy
from mathutils import *
-from math import pi,sin,degrees,radians,atan2,copysign,cos,acos
-from random import random,uniform,seed,choice,getstate,setstate
+from math import pi, sin, degrees, radians, atan2, copysign, cos, acos
+from math import floor, ceil
+from random import random, uniform, seed, choice, getstate, setstate, randint
from bpy.props import *
-from collections import deque
+from collections import deque, OrderedDict
+
+tau = 2 * pi
# Initialise the split error and axis vectors
splitError = 0.0
-zAxis = Vector((0,0,1))
-yAxis = Vector((0,1,0))
-xAxis = Vector((1,0,0))
+zAxis = Vector((0, 0, 1))
+yAxis = Vector((0, 1, 0))
+xAxis = Vector((1, 0, 0))
# This class will contain a part of the tree which needs to be extended and the required tree parameters
class stemSpline:
- def __init__(self,spline,curvature,curvatureV,segments,maxSegs,segLength,childStems,stemRadStart,stemRadEnd,splineNum):
+ def __init__(self, spline, curvature, curvatureV, attractUp, segments, maxSegs, segLength, childStems, stemRadStart, stemRadEnd, splineNum, ofst, pquat):
self.spline = spline
self.p = spline.bezier_points[-1]
self.curv = curvature
self.curvV = curvatureV
+ self.vertAtt = attractUp
self.seg = segments
self.segMax = maxSegs
self.segL = segLength
@@ -47,12 +52,17 @@ class stemSpline:
self.radS = stemRadStart
self.radE = stemRadEnd
self.splN = splineNum
+ self.offsetLen = ofst
+ self.patentQuat = pquat
+ self.curvSignx = 1
+ self.curvSigny = 1
+
# This method determines the quaternion of the end of the spline
def quat(self):
if len(self.spline.bezier_points) == 1:
- return ((self.spline.bezier_points[-1].handle_right - self.spline.bezier_points[-1].co).normalized()).to_track_quat('Z','Y')
+ return ((self.spline.bezier_points[-1].handle_right - self.spline.bezier_points[-1].co).normalized()).to_track_quat('Z', 'Y')
else:
- return ((self.spline.bezier_points[-1].co - self.spline.bezier_points[-2].co).normalized()).to_track_quat('Z','Y')
+ return ((self.spline.bezier_points[-1].co - self.spline.bezier_points[-2].co).normalized()).to_track_quat('Z', 'Y')
# Determine the declination
def dec(self):
tempVec = zAxis.copy()
@@ -62,31 +72,23 @@ class stemSpline:
def updateEnd(self):
self.p = self.spline.bezier_points[-1]
self.seg += 1
- # Determine the spread angle for a split
- def spreadAng(self):
- return radians(choice([-1,1])*(20 + 0.75*(30 + abs(degrees(self.dec()) - 90))*random()**2))
- # Determine the splitting angle for a split
- def splitAngle(self,splitAng,splitAngV):
- return max(0,splitAng+uniform(-splitAngV,splitAngV)-self.dec())
- # This is used to change the the curvature per segment of the spline
- def curvAdd(self,curvD):
- self.curv += curvD
# This class contains the data for a point where a new branch will sprout
class childPoint:
- def __init__(self,coords,quat,radiusPar,offset,lengthPar,parBone):
+ def __init__(self, coords, quat, radiusPar, offset, sOfst, lengthPar, parBone):
self.co = coords
self.quat = quat
self.radiusPar = radiusPar
self.offset = offset
+ self.stemOffset = sOfst
self.lengthPar = lengthPar
self.parBone = parBone
# This function calculates the shape ratio as defined in the paper
-def shapeRatio(shape,ratio,pruneWidthPeak=0.0,prunePowerHigh=0.0,prunePowerLow=0.0):
+def shapeRatio(shape, ratio, pruneWidthPeak=0.0, prunePowerHigh=0.0, prunePowerLow=0.0, custom=None):
if shape == 0:
- return 0.2 + 0.8*ratio
+ return 0.05 + 0.95*ratio #0.2 + 0.8*ratio
elif shape == 1:
return 0.2 + 0.8*sin(pi*ratio)
elif shape == 2:
@@ -97,9 +99,9 @@ def shapeRatio(shape,ratio,pruneWidthPeak=0.0,prunePowerHigh=0.0,prunePowerLow=0
return 0.5 + 0.5*ratio
elif shape == 5:
if ratio <= 0.7:
- return ratio/0.7
+ return 0.05 + 0.95 * ratio/0.7
else:
- return (1.0 - ratio)/0.3
+ return 0.05 + 0.95 * (1.0 - ratio)/0.3
elif shape == 6:
return 1.0 - 0.8*ratio
elif shape == 7:
@@ -108,6 +110,23 @@ def shapeRatio(shape,ratio,pruneWidthPeak=0.0,prunePowerHigh=0.0,prunePowerLow=0
else:
return 0.5 + 0.5*(1.0 - ratio)/0.3
elif shape == 8:
+ r = 1 - ratio
+ if r == 1:
+ v = custom[3]
+ elif r >= custom[2]:
+ pos = (r - custom[2]) / (1 - custom[2])
+ #if (custom[0] >= custom[1] <= custom[3]) or (custom[0] <= custom[1] >= custom[3]):
+ pos = pos * pos
+ v = (pos * (custom[3] - custom[1])) + custom[1]
+ else:
+ pos = r / custom[2]
+ #if (custom[0] >= custom[1] <= custom[3]) or (custom[0] <= custom[1] >= custom[3]):
+ pos = 1 - (1 - pos) * (1 - pos)
+ v = (pos * (custom[1] - custom[0])) + custom[0]
+
+ return v
+
+ elif shape == 9:
if (ratio < (1 - pruneWidthPeak)) and (ratio > 0.0):
return ((ratio/(1 - pruneWidthPeak))**prunePowerHigh)
elif (ratio >= (1 - pruneWidthPeak)) and (ratio < 1.0):
@@ -115,13 +134,32 @@ def shapeRatio(shape,ratio,pruneWidthPeak=0.0,prunePowerHigh=0.0,prunePowerLow=0
else:
return 0.0
+ elif shape == 10:
+ return 0.5 + 0.5 * (1 - ratio)
+
# This function determines the actual number of splits at a given point using the global error
def splits(n):
global splitError
- nEff = round(n + splitError,0)
+ nEff = round(n + splitError, 0)
splitError -= (nEff - n)
return int(nEff)
+def splits2(n):
+ r = random()
+ if r < n:
+ return 1
+ else:
+ return 0
+
+def splits3(n):
+ ni = int(n)
+ nf = n - int(n)
+ r = random()
+ if r < nf:
+ return ni + 1
+ else:
+ return ni + 0
+
# Determine the declination from a given quaternion
def declination(quat):
tempVec = zAxis.copy()
@@ -129,143 +167,260 @@ def declination(quat):
tempVec.normalize()
return degrees(acos(tempVec.z))
-# Determine the length of a child stem
-def lengthChild(lMax,offset,lPar,shape=False,lBase=None):
- if shape:
- return lPar*lMax*shapeRatio(shape,(lPar - offset) / max((lPar - lBase), 1e-6))
- else:
- return lMax*(lPar - 0.6*offset)
-
-# Find the actual downAngle taking into account the special case
-def downAngle(downAng,downAngV,lPar=None,offset=None,lBase=None):
- if downAngV < 0:
- return downAng + (uniform(-downAngV,downAngV)*(1 - 2*shapeRatio(0,(lPar - offset) / max((lPar - lBase), 1e-6))))
- else:
- return downAng + uniform(-downAngV,downAngV)
-
-# Returns the rotation matrix equivalent to i rotations by 2*pi/(n+1)
-def splitRotMat(n,i):
- return Matrix.Rotation(2*i*pi/(n+1),3,'Z')
-
-# Returns the split angle
-def angleSplit(splitAng,splitAngV,quat):
- return max(0,splitAng+uniform(-splitAngV,splitAngV)-declination(quat))
-
-# Returns number of stems a stem will sprout
-def stems(stemsMax,lPar,offset,lChild=False,lChildMax=None):
- if lChild:
- return stemsMax*(0.2 + 0.8*(lChild/lPar)/lChildMax)
- else:
- return stemsMax*(1.0 - 0.5*offset/lPar)
-
-# Returns the spreading angle
-def spreadAng(dec):
- return radians(choice([-1,1])*(20 + 0.75*(30 + abs(dec - 90))*random()**2))
-
# Determines the angle of upward rotation of a segment due to attractUp
-def curveUp(attractUp,quat,curveRes):
+def curveUp(attractUp, quat, curveRes):
tempVec = yAxis.copy()
tempVec.rotate(quat)
tempVec.normalize()
- return attractUp*radians(declination(quat))*abs(tempVec.z)/curveRes
+
+ dec = radians(declination(quat))
+ curveUpAng = attractUp*dec*abs(tempVec.z)/curveRes
+ if (-dec + curveUpAng) < -pi:
+ curveUpAng = -pi + dec
+ if (dec - curveUpAng) < 0:
+ curveUpAng = dec
+ return curveUpAng
# Evaluate a bezier curve for the parameter 0<=t<=1 along its length
-def evalBez(p1,h1,h2,p2,t):
+def evalBez(p1, h1, h2, p2, t):
return ((1-t)**3)*p1 + (3*t*(1-t)**2)*h1 + (3*(t**2)*(1-t))*h2 + (t**3)*p2
# Evaluate the unit tangent on a bezier curve for t
-def evalBezTan(p1,h1,h2,p2,t):
+def evalBezTan(p1, h1, h2, p2, t):
return ((-3*(1-t)**2)*p1 + (-6*t*(1-t) + 3*(1-t)**2)*h1 + (-3*(t**2) + 6*t*(1-t))*h2 + (3*t**2)*p2).normalized()
# Determine the range of t values along a splines length where child stems are formed
-def findChildPoints(stemList,numChild):
+def findChildPoints(stemList, numChild):
numPoints = sum([len(n.spline.bezier_points) for n in stemList])
numSplines = len(stemList)
numSegs = numPoints - numSplines
numPerSeg = numChild/numSegs
- numMain = round(numPerSeg*stemList[0].segMax,0)
+ numMain = round(numPerSeg*stemList[0].segMax, 0)
return [(a+1)/(numMain) for a in range(int(numMain))]
+def findChildPoints2(stemList, numChild):
+ return [(a+1)/(numChild) for a in range(int(numChild))]
+
# Find the coordinates, quaternion and radius for each t on the stem
-def interpStem(stem,tVals,lPar,parRad):
+def interpStem1(stem, tVals, lPar, parRad):
+ points = stem.spline.bezier_points
+ numPoints = len(points)
+ checkVal = (stem.segMax - (numPoints - 1)) / stem.segMax
+ # Loop through all the parametric values to be determined
tempList = deque()
- addpoint = tempList.append
- checkVal = (stem.segMax - len(stem.spline.bezier_points) + 1)/stem.segMax
+ for t in tVals:
+ if t == 1.0:
+ index = numPoints-2
+ coord = points[-1].co
+ quat = (points[-1].handle_right - points[-1].co).to_track_quat('Z', 'Y')
+ radius = points[-1].radius
+
+ tempList.append(childPoint(coord, quat, (parRad, radius), t, lPar, 'bone'+(str(stem.splN).rjust(3, '0'))+'.'+(str(index).rjust(3, '0'))))
+
+ elif (t >= checkVal) and (t < 1.0):
+ scaledT = (t - checkVal) / ((1 - checkVal) + .0001)
+ length = (numPoints-1)*scaledT
+ index = int(length)
+
+ tTemp = length - index
+ coord = evalBez(points[index].co, points[index].handle_right, points[index+1].handle_left, points[index+1].co, tTemp)
+ quat = (evalBezTan(points[index].co, points[index].handle_right, points[index+1].handle_left, points[index+1].co, tTemp)).to_track_quat('Z', 'Y')
+ radius = (1-tTemp)*points[index].radius + tTemp*points[index+1].radius # Not sure if this is the parent radius at the child point or parent start radius
+
+ tempList.append(childPoint(coord, quat, (parRad, radius), t, lPar, 'bone'+(str(stem.splN).rjust(3, '0'))+'.'+(str(index).rjust(3, '0'))))
+
+ return tempList
+
+def interpStem(stem, tVals, lPar, parRad, maxOffset, baseSize):
points = stem.spline.bezier_points
- numPoints = len(stem.spline.bezier_points)
+ numSegs = len(points) - 1
+ stemLen = stem.segL * numSegs
+
+ checkBottom = stem.offsetLen / maxOffset
+ checkTop = checkBottom + (stemLen / maxOffset)
+
# Loop through all the parametric values to be determined
+ tempList = deque()
for t in tVals:
- if (t >= checkVal) and (t < 1.0):
- scaledT = (t - checkVal) / max(tVals[-1] - checkVal, 1e-6)
- length = (numPoints-1)*t#scaledT
+ if (t >= checkBottom) and (t <= checkTop) and (t < 1.0):
+ scaledT = (t - checkBottom) / (checkTop - checkBottom)
+ ofst = ((t - baseSize) / (checkTop - baseSize)) * (1 - baseSize) + baseSize
+
+ length = numSegs * scaledT
index = int(length)
- if scaledT == 1.0:
- coord = points[-1].co
- quat = (points[-1].handle_right - points[-1].co).to_track_quat('Z','Y')
- radius = parRad#points[-2].radius
- else:
- tTemp = length - index
- coord = evalBez(points[index].co,points[index].handle_right,points[index+1].handle_left,points[index+1].co,tTemp)
- quat = (evalBezTan(points[index].co,points[index].handle_right,points[index+1].handle_left,points[index+1].co,tTemp)).to_track_quat('Z','Y')
- radius = (1-tTemp)*points[index].radius + tTemp*points[index+1].radius # Not sure if this is the parent radius at the child point or parent start radius
- addpoint(childPoint(coord,quat,(parRad, radius),t*lPar,lPar,'bone'+(str(stem.splN).rjust(3,'0'))+'.'+(str(index).rjust(3,'0'))))
+ tTemp = length - index
+
+ coord = evalBez(points[index].co, points[index].handle_right, points[index+1].handle_left, points[index+1].co, tTemp)
+ quat = (evalBezTan(points[index].co, points[index].handle_right, points[index+1].handle_left, points[index+1].co, tTemp)).to_track_quat('Z', 'Y')
+ radius = (1-tTemp)*points[index].radius + tTemp*points[index+1].radius # Not sure if this is the parent radius at the child point or parent start radius
+
+ tempList.append(childPoint(coord, quat, (parRad, radius), t, ofst, lPar, 'bone'+(str(stem.splN).rjust(3, '0'))+'.'+(str(index).rjust(3, '0'))))
+
+ #add stem at tip
+ index = numSegs-1
+ coord = points[-1].co
+ quat = (points[-1].handle_right - points[-1].co).to_track_quat('Z', 'Y')
+ radius = points[-1].radius
+ tempList.append(childPoint(coord, quat, (parRad, radius), 1, 1, lPar, 'bone'+(str(stem.splN).rjust(3, '0'))+'.'+(str(index).rjust(3, '0'))))
+
return tempList
+# round down bone number
+def roundBone(bone, step):
+ bone_i = bone[:-3]
+ bone_n = int(bone[-3:])
+ bone_n = int(bone_n / step) * step
+ return bone_i + str(bone_n).rjust(3, '0')
+
# Convert a list of degrees to radians
def toRad(list):
return [radians(a) for a in list]
+def anglemean(a1, a2, fac):
+ x1 = sin(a1)
+ y1 = cos(a1)
+ x2 = sin(a2)
+ y2 = cos(a2)
+ x = x1 + (x2 - x1) * fac
+ y = y1 + (y2 - y1) * fac
+ return atan2(x, y)
+
+
# This is the function which extends (or grows) a given stem.
-def growSpline(stem,numSplit,splitAng,splitAngV,splineList,attractUp,hType,splineToBone):
+def growSpline(n, stem, numSplit, splitAng, splitAngV, splineList, hType, splineToBone, closeTip, kp, splitHeight, outAtt, stemsegL,
+ lenVar, taperCrown, boneStep, rotate, rotateV):
+
+ #curv at base
+ sCurv = stem.curv
+ if (n == 0) and (kp <= splitHeight):
+ sCurv = 0.0
+
+ #curveangle = sCurv + (uniform(-stem.curvV, stem.curvV) * kp)
+ #curveVar = uniform(-stem.curvV, stem.curvV) * kp
+ curveangle = sCurv + (uniform(0, stem.curvV) * kp * stem.curvSignx)
+ curveVar = uniform(0, stem.curvV) * kp * stem.curvSigny
+ stem.curvSignx *= -1
+ stem.curvSigny *= -1
+
+ curveVarMat = Matrix.Rotation(curveVar, 3, 'Y')
+
# First find the current direction of the stem
dir = stem.quat()
+
+ if n == 0:
+ adir = zAxis.copy()
+ adir.rotate(dir)
+
+ ry = atan2(adir[0], adir[2])
+ adir.rotate(Euler((0, -ry, 0)))
+ rx = atan2(adir[1], adir[2])
+
+ dir = Euler((-rx, ry, 0), 'XYZ')
+
+ #length taperCrown
+ if n == 0:
+ dec = declination(dir) / 180
+ dec = dec ** 2
+ tf = 1 - (dec * taperCrown * 30)
+ tf = max(.1, tf)
+ else:
+ tf = 1.0
+
+ #outward attraction
+ if (n > 0) and (kp > 0) and (outAtt > 0):
+ p = stem.p.co.copy()
+ d = atan2(p[0], -p[1]) + tau
+ edir = dir.to_euler('XYZ', Euler((0, 0, d), 'XYZ'))
+ d = anglemean(edir[2], d, (kp * outAtt))
+ dirv = Euler((edir[0], edir[1], d), 'XYZ')
+ dir = dirv.to_quaternion()
+
+ #parent weight
+# parWeight = kp * degrees(stem.curvV) * pi
+# #parWeight = parWeight * kp
+# #parWeight = kp
+# if (n > 1) and (parWeight != 0):
+# d1 = zAxis.copy()
+# d2 = zAxis.copy()
+# d1.rotate(dir)
+# d2.rotate(stem.patentQuat)
+#
+# x = d1[0] + ((d2[0] - d1[0]) * parWeight)
+# y = d1[1] + ((d2[1] - d1[1]) * parWeight)
+# z = d1[2] + ((d2[2] - d1[2]) * parWeight)
+#
+# d3 = Vector((x, y, z))
+# dir = d3.to_track_quat('Z', 'Y')
+
# If the stem splits, we need to add new splines etc
if numSplit > 0:
# Get the curve data
cuData = stem.spline.id_data.name
cu = bpy.data.curves[cuData]
+
+ #calc split angles
+ angle = choice([-1, 1]) * (splitAng + uniform(-splitAngV, splitAngV))
+ if n > 0:
+ #make branches flatter
+ angle *= max(1 - declination(dir) / 90, 0) * .67 + .33
+ spreadangle = choice([-1, 1]) * (splitAng + uniform(-splitAngV, splitAngV))
+
+ #branchRotMat = Matrix.Rotation(radians(uniform(0, 360)), 3, 'Z')
+ if not hasattr(stem, 'rLast'):
+ stem.rLast = radians(uniform(0, 360))
+
+ br = rotate[0] + uniform(-rotateV[0], rotateV[0])
+ branchRot = stem.rLast + br
+ branchRotMat = Matrix.Rotation(branchRot, 3, 'Z')
+ stem.rLast = branchRot
+
# Now for each split add the new spline and adjust the growth direction
for i in range(numSplit):
+ #find split scale
+ lenV = uniform(1 - lenVar, 1 + lenVar)
+ bScale = min(lenV * tf, 1)
+
newSpline = cu.splines.new('BEZIER')
newPoint = newSpline.bezier_points[-1]
- (newPoint.co,newPoint.handle_left_type,newPoint.handle_right_type) = (stem.p.co,'VECTOR','VECTOR')
- newPoint.radius = stem.radS*(1 - stem.seg/stem.segMax) + stem.radE*(stem.seg/stem.segMax)
-
+ (newPoint.co, newPoint.handle_left_type, newPoint.handle_right_type) = (stem.p.co, 'VECTOR', 'VECTOR')
+ newPoint.radius = (stem.radS*(1 - stem.seg/stem.segMax) + stem.radE*(stem.seg/stem.segMax)) * bScale
# Here we make the new "sprouting" stems diverge from the current direction
- angle = stem.splitAngle(splitAng,splitAngV)
- divRotMat = Matrix.Rotation(angle + stem.curv + uniform(-stem.curvV,stem.curvV),3,'X')#CurveUP should go after curve is applied
- dirVec = zAxis.copy()
- dirVec.rotate(divRotMat)
- dirVec.rotate(splitRotMat(numSplit,i+1))
- dirVec.rotate(dir)
-
- # if attractUp != 0.0: # Shouldn't have a special case as this will mess with random number generation
- divRotMat = Matrix.Rotation(angle + stem.curv + uniform(-stem.curvV,stem.curvV),3,'X')
+ divRotMat = Matrix.Rotation(angle + curveangle, 3, 'X')
dirVec = zAxis.copy()
dirVec.rotate(divRotMat)
- dirVec.rotate(splitRotMat(numSplit,i+1))
- dirVec.rotate(dir)
-
- #Different version of the commented code above. We could use the inbuilt vector rotations but given this is a special case, it can be quicker to initialise the vector to the correct value.
-# angle = stem.splitAngle(splitAng,splitAngV)
-# curveUpAng = curveUp(attractUp,dir,stem.segMax)
-# angleX = angle + stem.curv + uniform(-stem.curvV,stem.curvV) - curveUpAng
-# angleZ = 2*i*pi/(numSplit+1)
-# dirVec = Vector((sin(angleX)*sin(angleZ), -sin(angleX)*cos(angleZ), cos(angleX)))
-# dirVec.rotate(dir)
+
+ #horizontal curvature variation
+ dirVec.rotate(curveVarMat)
+
+ if n == 0: #Special case for trunk splits
+ dirVec.rotate(branchRotMat)
+
+ ang = pi - ((tau) / (numSplit + 1)) * (i+1)
+ dirVec.rotate(Matrix.Rotation(ang, 3, 'Z'))
# Spread the stem out in a random fashion
- spreadMat = Matrix.Rotation(spreadAng(degrees(dirVec.z)),3,'Z')
- dirVec.rotate(spreadMat)
+ spreadMat = Matrix.Rotation(spreadangle, 3, 'Y')
+ if n != 0: #Special case for trunk splits
+ dirVec.rotate(spreadMat)
+
+ dirVec.rotate(dir)
+
# Introduce upward curvature
upRotAxis = xAxis.copy()
- upRotAxis.rotate(dirVec.to_track_quat('Z','Y'))
- curveUpAng = curveUp(attractUp,dirVec.to_track_quat('Z','Y'),stem.segMax)
- upRotMat = Matrix.Rotation(-curveUpAng,3,upRotAxis)
+ upRotAxis.rotate(dirVec.to_track_quat('Z', 'Y'))
+ curveUpAng = curveUp(stem.vertAtt, dirVec.to_track_quat('Z', 'Y'), stem.segMax)
+ upRotMat = Matrix.Rotation(-curveUpAng, 3, upRotAxis)
dirVec.rotate(upRotMat)
+
# Make the growth vec the length of a stem segment
dirVec.normalize()
- dirVec *= stem.segL
+
+ #split length variation
+ stemL = stemsegL * lenV
+ dirVec *= stemL * tf
+ ofst = stem.offsetLen + (stem.segL * (len(stem.spline.bezier_points) - 1))
+
+ ##dirVec *= stem.segL
# Get the end point position
end_co = stem.p.co.copy()
@@ -273,112 +428,196 @@ def growSpline(stem,numSplit,splitAng,splitAngV,splineList,attractUp,hType,splin
# Add the new point and adjust its coords, handles and radius
newSpline.bezier_points.add()
newPoint = newSpline.bezier_points[-1]
- (newPoint.co,newPoint.handle_left_type,newPoint.handle_right_type) = (end_co + dirVec,hType,hType)
- newPoint.radius = stem.radS*(1 - (stem.seg + 1)/stem.segMax) + stem.radE*((stem.seg + 1)/stem.segMax)
+ (newPoint.co, newPoint.handle_left_type, newPoint.handle_right_type) = (end_co + dirVec, hType, hType)
+ newPoint.radius = (stem.radS*(1 - (stem.seg + 1)/stem.segMax) + stem.radE*((stem.seg + 1)/stem.segMax)) * bScale
+ if (stem.seg == stem.segMax-1) and closeTip:
+ newPoint.radius = 0.0
# If this isn't the last point on a stem, then we need to add it to the list of stems to continue growing
- if stem.seg != stem.segMax:
- splineList.append(stemSpline(newSpline,stem.curv-angle/(stem.segMax-stem.seg),stem.curvV,stem.seg+1,stem.segMax,stem.segL,stem.children,stem.radS,stem.radE,len(cu.splines)-1))
- splineToBone.append('bone'+(str(stem.splN)).rjust(3,'0')+'.'+(str(len(stem.spline.bezier_points)-2)).rjust(3,'0'))
+ #print(stem.seg != stem.segMax, stem.seg, stem.segMax)
+ #if stem.seg != stem.segMax: # if probs not nessesary
+ nstem = stemSpline(newSpline, stem.curv, stem.curvV, stem.vertAtt, stem.seg+1, stem.segMax, stemL, stem.children,
+ stem.radS * bScale, stem.radE * bScale, len(cu.splines)-1, ofst, stem.quat())
+ nstem.splitlast = 1#numSplit #keep track of numSplit for next stem
+ nstem.rLast = branchRot + pi
+ splineList.append(nstem)
+ bone = 'bone'+(str(stem.splN)).rjust(3, '0')+'.'+(str(len(stem.spline.bezier_points)-2)).rjust(3, '0')
+ bone = roundBone(bone, boneStep[n])
+ splineToBone.append((bone, False, True, len(stem.spline.bezier_points)-2))
+
# The original spline also needs to keep growing so adjust its direction too
- angle = stem.splitAngle(splitAng,splitAngV)
- divRotMat = Matrix.Rotation(angle + stem.curv + uniform(-stem.curvV,stem.curvV),3,'X')
+ divRotMat = Matrix.Rotation(-angle + curveangle, 3, 'X')
dirVec = zAxis.copy()
dirVec.rotate(divRotMat)
+
+ #horizontal curvature variation
+ dirVec.rotate(curveVarMat)
+
+ if n == 0: #Special case for trunk splits
+ dirVec.rotate(branchRotMat)
+
+ #spread
+ spreadMat = Matrix.Rotation(-spreadangle, 3, 'Y')
+ if n != 0: #Special case for trunk splits
+ dirVec.rotate(spreadMat)
+
dirVec.rotate(dir)
- spreadMat = Matrix.Rotation(spreadAng(degrees(dirVec.z)),3,'Z')
- dirVec.rotate(spreadMat)
+
+ stem.splitlast = 1#numSplit #keep track of numSplit for next stem
+
else:
# If there are no splits then generate the growth direction without accounting for spreading of stems
dirVec = zAxis.copy()
- #curveUpAng = curveUp(attractUp,dir,stem.segMax)
- divRotMat = Matrix.Rotation(stem.curv + uniform(-stem.curvV,stem.curvV),3,'X')
+ divRotMat = Matrix.Rotation(curveangle, 3, 'X')
dirVec.rotate(divRotMat)
- #dirVec = Vector((0,-sin(stem.curv - curveUpAng),cos(stem.curv - curveUpAng)))
+
+ #horizontal curvature variation
+ dirVec.rotate(curveVarMat)
+
dirVec.rotate(dir)
+
+ stem.splitlast = 0#numSplit #keep track of numSplit for next stem
+
+ # Introduce upward curvature
upRotAxis = xAxis.copy()
- upRotAxis.rotate(dirVec.to_track_quat('Z','Y'))
- curveUpAng = curveUp(attractUp,dirVec.to_track_quat('Z','Y'),stem.segMax)
- upRotMat = Matrix.Rotation(-curveUpAng,3,upRotAxis)
+ upRotAxis.rotate(dirVec.to_track_quat('Z', 'Y'))
+ curveUpAng = curveUp(stem.vertAtt, dirVec.to_track_quat('Z', 'Y'), stem.segMax)
+ upRotMat = Matrix.Rotation(-curveUpAng, 3, upRotAxis)
dirVec.rotate(upRotMat)
+
dirVec.normalize()
- dirVec *= stem.segL
+ dirVec *= stem.segL * tf
# Get the end point position
end_co = stem.p.co.copy()
stem.spline.bezier_points.add()
newPoint = stem.spline.bezier_points[-1]
- (newPoint.co,newPoint.handle_left_type,newPoint.handle_right_type) = (end_co + dirVec,hType,hType)
+ (newPoint.co, newPoint.handle_left_type, newPoint.handle_right_type) = (end_co + dirVec, hType, hType)
newPoint.radius = stem.radS*(1 - (stem.seg + 1)/stem.segMax) + stem.radE*((stem.seg + 1)/stem.segMax)
+ if (stem.seg == stem.segMax-1) and closeTip:
+ newPoint.radius = 0.0
# There are some cases where a point cannot have handles as VECTOR straight away, set these now.
- if numSplit != 0:
- tempPoint = stem.spline.bezier_points[-2]
- (tempPoint.handle_left_type,tempPoint.handle_right_type) = ('VECTOR','VECTOR')
if len(stem.spline.bezier_points) == 2:
tempPoint = stem.spline.bezier_points[0]
- (tempPoint.handle_left_type,tempPoint.handle_right_type) = ('VECTOR','VECTOR')
+ (tempPoint.handle_left_type, tempPoint.handle_right_type) = ('VECTOR', 'VECTOR')
# Update the last point in the spline to be the newly added one
stem.updateEnd()
#return splineList
-def genLeafMesh(leafScale,leafScaleX,loc,quat,index,downAngle,downAngleV,rotate,rotateV,oldRot,bend,leaves, leafShape):
+def genLeafMesh(leafScale, leafScaleX, leafScaleT, leafScaleV, loc, quat, offset, index, downAngle, downAngleV, rotate, rotateV, oldRot,
+ bend, leaves, leafShape, leafangle, horzLeaves):
if leafShape == 'hex':
- verts = [Vector((0,0,0)),Vector((0.5,0,1/3)),Vector((0.5,0,2/3)),Vector((0,0,1)),Vector((-0.5,0,2/3)),Vector((-0.5,0,1/3))]
- edges = [[0,1],[1,2],[2,3],[3,4],[4,5],[5,0],[0,3]]
- faces = [[0,1,2,3],[0,3,4,5]]
+ verts = [Vector((0, 0, 0)), Vector((0.5, 0, 1/3)), Vector((0.5, 0, 2/3)), Vector((0, 0, 1)), Vector((-0.5, 0, 2/3)), Vector((-0.5, 0, 1/3))]
+ edges = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0], [0, 3]]
+ faces = [[0, 1, 2, 3], [0, 3, 4, 5]]
elif leafShape == 'rect':
- verts = [Vector((1,0,0)),Vector((1,0,1)),Vector((-1,0,1)),Vector((-1,0,0))]
- edges = [[0,1],[1,2],[2,3],[3,0]]
- faces = [[0,1,2,3],]
- #faces = [[0,1,5],[1,2,4,5],[2,3,4]]
+ #verts = [Vector((1, 0, 0)), Vector((1, 0, 1)), Vector((-1, 0, 1)), Vector((-1, 0, 0))]
+ verts = [Vector((.5, 0, 0)), Vector((.5, 0, 1)), Vector((-.5, 0, 1)), Vector((-.5, 0, 0))]
+ edges = [[0, 1], [1, 2], [2, 3], [3, 0]]
+ faces = [[0, 1, 2, 3]]
+ elif leafShape == 'dFace':
+ verts = [Vector((.5, .5, 0)), Vector((.5, -.5, 0)), Vector((-.5, -.5, 0)), Vector((-.5, .5, 0))]
+ edges = [[0, 1], [1, 2], [2, 3], [3, 0]]
+ faces = [[0, 3, 2, 1]]
+ elif leafShape == 'dVert':
+ verts = [Vector((0, 0, 1))]
+ edges = []
+ faces = []
vertsList = []
facesList = []
+ normal = Vector((0, 0, 1))
- # If the special -ve flag is used we need a different rotation of the leaf geometry
if leaves < 0:
- rotMat = Matrix.Rotation(oldRot,3,'Y')
- oldRot += rotate/abs(leaves)
+ rotMat = Matrix.Rotation(oldRot, 3, 'Y')
else:
- oldRot += rotate+uniform(-rotateV,rotateV)
- downRotMat = Matrix.Rotation(downAngle+uniform(-downAngleV,downAngleV),3,'X')
- rotMat = Matrix.Rotation(oldRot,3,'Z')
+ rotMat = Matrix.Rotation(oldRot, 3, 'Z')
- normal = yAxis.copy()
- #dirVec = zAxis.copy()
- orientationVec = zAxis.copy()
+ # 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 < 0.0:
+ oldRot = -copysign(rotate + uniform(-rotateV, rotateV), oldRot)
+ else:
+ # If the special -ve flag for leaves is used we need a different rotation of the leaf geometry
+ if leaves == -1:
+ #oldRot = 0
+ rotMat = Matrix.Rotation(0, 3, 'Y')
+ elif leaves < -1:
+ oldRot += rotate / (-leaves - 1)
+ else:
+ oldRot += rotate + uniform(-rotateV, rotateV)
- # If the bending of the leaves is used we need to rotated them differently
+# if leaves < 0:
+# rotMat = Matrix.Rotation(oldRot, 3, 'Y')
+# else:
+# rotMat = Matrix.Rotation(oldRot, 3, 'Z')
+
+ if leaves >= 0:
+ #downRotMat = Matrix.Rotation(downAngle+uniform(-downAngleV, downAngleV), 3, 'X')
+
+ if downAngleV > 0.0:
+ downV = -downAngleV * offset
+ else:
+ downV = uniform(-downAngleV, downAngleV)
+ downRotMat = Matrix.Rotation(downAngle + downV, 3, 'X')
+
+ #leaf scale variation
+ if (leaves < -1) and (rotate != 0):
+ f = 1 - abs((oldRot - (rotate / (-leaves - 1))) / (rotate / 2))
+ else:
+ f = offset
+
+ if leafScaleT < 0:
+ leafScale = leafScale * (1 - (1 - f) * -leafScaleT)
+ else:
+ leafScale = leafScale * (1 - f * leafScaleT)
+
+ leafScale = leafScale * uniform(1 - leafScaleV, 1 + leafScaleV)
+
+ if leafShape == 'dFace':
+ leafScale = leafScale * .1
+
+ # If the bending of the leaves is used we need to rotate them differently
if (bend != 0.0) and (leaves >= 0):
-# normal.rotate(downRotMat)
-# orientationVec.rotate(downRotMat)
-#
-# normal.rotate(rotMat)
-# orientationVec.rotate(rotMat)
+ normal = yAxis.copy()
+ orientationVec = zAxis.copy()
normal.rotate(quat)
orientationVec.rotate(quat)
- thetaPos = atan2(loc.y,loc.x)
- thetaBend = thetaPos - atan2(normal.y,normal.x)
- rotateZ = Matrix.Rotation(bend*thetaBend,3,'Z')
+ thetaPos = atan2(loc.y, loc.x)
+ thetaBend = thetaPos - atan2(normal.y, normal.x)
+ rotateZ = Matrix.Rotation(bend*thetaBend, 3, 'Z')
normal.rotate(rotateZ)
orientationVec.rotate(rotateZ)
- phiBend = atan2((normal.xy).length,normal.z)
- orientation = atan2(orientationVec.y,orientationVec.x)
- rotateZOrien = Matrix.Rotation(orientation,3,'X')
+ phiBend = atan2((normal.xy).length, normal.z)
+ orientation = atan2(orientationVec.y, orientationVec.x)
+ rotateZOrien = Matrix.Rotation(orientation, 3, 'X')
- rotateX = Matrix.Rotation(bend*phiBend,3,'Z')
+ rotateX = Matrix.Rotation(bend*phiBend, 3, 'Z')
- rotateZOrien2 = Matrix.Rotation(-orientation,3,'X')
+ rotateZOrien2 = Matrix.Rotation(-orientation, 3, 'X')
# For each of the verts we now rotate and scale them, then append them to the list to be added to the mesh
for v in verts:
-
v.z *= leafScale
+ v.y *= leafScale
v.x *= leafScaleX*leafScale
+ v.rotate(Euler((0, 0, radians(180))))
+
+ #leafangle
+ v.rotate(Matrix.Rotation(radians(-leafangle), 3, 'X'))
+
+ if rotate < 0:
+ v.rotate(Euler((0, 0, radians(90))))
+ if oldRot < 0:
+ v.rotate(Euler((0, 0, radians(180))))
+
+ if (leaves > 0) and (rotate > 0) and horzLeaves:
+ nRotMat = Matrix.Rotation(-oldRot + rotate, 3, 'Z')
+ v.rotate(nRotMat)
+
if leaves > 0:
v.rotate(downRotMat)
@@ -392,18 +631,23 @@ def genLeafMesh(leafScale,leafScaleX,loc,quat,index,downAngle,downAngleV,rotate,
v.rotate(rotateX)
v.rotate(rotateZOrien2)
- #v.rotate(quat)
- for v in verts:
- v += loc
- vertsList.append([v.x,v.y,v.z])
+ if leafShape == 'dVert':
+ normal = verts[0]
+ normal.normalize()
+ v = loc
+ vertsList.append([v.x, v.y, v.z])
+ else:
+ for v in verts:
+ v += loc
+ vertsList.append([v.x, v.y, v.z])
+ for f in faces:
+ facesList.append([f[0] + index, f[1] + index, f[2] + index, f[3] + index])
- for f in faces:
- facesList.append([f[0] + index,f[1] + index,f[2] + index,f[3] + index])
- return vertsList,facesList,oldRot
+ return vertsList, facesList, normal, oldRot
-def create_armature(armAnim, childP, cu, frameRate, leafMesh, leafObj, leafShape, leaves, levelCount, splineToBone,
- treeOb, windGust, windSpeed):
+def 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):
arm = bpy.data.armatures.new('tree')
armOb = bpy.data.objects.new('treeArm', arm)
bpy.context.scene.objects.link(armOb)
@@ -415,7 +659,11 @@ def create_armature(armAnim, childP, cu, frameRate, leafMesh, leafObj, leafShape
arm.use_deform_delay = True
# Add the armature modifier to the curve
armMod = treeOb.modifiers.new('windSway', 'ARMATURE')
- #armMod.use_apply_on_spline = True
+ if previewArm:
+ armMod.show_viewport = False
+ arm.draw_type = 'WIRE'
+ treeOb.hide = True
+ armMod.use_apply_on_spline = True
armMod.object = armOb
armMod.use_bone_envelopes = True
armMod.use_vertex_groups = False # curves don't have vertex groups (yet)
@@ -423,109 +671,241 @@ def create_armature(armAnim, childP, cu, frameRate, leafMesh, leafObj, leafShape
if leaves:
armMod = leafObj.modifiers.new('windSway', 'ARMATURE')
armMod.object = armOb
- armMod.use_bone_envelopes = True
- armMod.use_vertex_groups = True
+ armMod.use_bone_envelopes = False
+ armMod.use_vertex_groups = True
# Make sure all objects are deselected (may not be required?)
for ob in bpy.data.objects:
ob.select = False
+ fps = bpy.context.scene.render.fps
+ animSpeed = (24 / fps) * frameRate
+
# Set the armature as active and go to edit mode to add bones
bpy.context.scene.objects.active = armOb
bpy.ops.object.mode_set(mode='EDIT')
- masterBones = []
- offsetVal = 0
# For all the splines in the curve we need to add bones at each bezier point
for i, parBone in enumerate(splineToBone):
- s = cu.splines[i]
- b = None
- # Get some data about the spline like length and number of points
- numPoints = len(s.bezier_points) - 1
- splineL = numPoints * ((s.bezier_points[0].co - s.bezier_points[1].co).length)
- # Set the random phase difference of the animation
- bxOffset = uniform(0, 2 * pi)
- byOffset = uniform(0, 2 * pi)
- # Set the phase multiplier for the spline
- bMult = (s.bezier_points[0].radius / max(splineL, 1e-6)) * (1 / 15) * (1 / frameRate)
- # For all the points in the curve (less the last) add a bone and name it by the spline it will affect
- for n in range(numPoints):
- oldBone = b
- boneName = 'bone' + (str(i)).rjust(3, '0') + '.' + (str(n)).rjust(3, '0')
- b = arm.edit_bones.new(boneName)
- b.head = s.bezier_points[n].co
- b.tail = s.bezier_points[n + 1].co
-
- b.head_radius = s.bezier_points[n].radius
- b.tail_radius = s.bezier_points[n + 1].radius
- b.envelope_distance = 0.001 #0.001
-
- # If there are leaves then we need a new vertex group so they will attach to the bone
- if (len(levelCount) > 1) and (i >= levelCount[-2]) and leafObj:
- leafObj.vertex_groups.new(boneName)
- elif (len(levelCount) == 1) and leafObj:
- leafObj.vertex_groups.new(boneName)
- # If this is first point of the spline then it must be parented to the level above it
- if n == 0:
- if parBone:
- b.parent = arm.edit_bones[parBone]
- # if len(parBone) > 11:
- # b.use_connect = True
- # Otherwise, we need to attach it to the previous bone in the spline
- else:
- b.parent = oldBone
- b.use_connect = True
- # If there isn't a previous bone then it shouldn't be attached
- if not oldBone:
- b.use_connect = False
- #tempList.append(b)
-
- # Add the animation to the armature if required
+ if (i < levelCount[armLevels]) or (armLevels == -1) or (not makeMesh):
+ s = cu.splines[i]
+ b = None
+ # Get some data about the spline like length and number of points
+ numPoints = len(s.bezier_points) - 1
+
+ #find branching level
+ level = 0
+ for l, c in enumerate(levelCount):
+ if i < c:
+ level = l
+ break
+ level = min(level, 3)
+
+ step = boneStep[level]
+
+ # Calculate things for animation
if armAnim:
- # Define all the required parameters of the wind sway by the dimension of the spline
- a0 = 4 * splineL * (1 - n / (numPoints + 1)) / max(s.bezier_points[n].radius, 1e-6)
- a1 = (windSpeed / 50) * a0
- a2 = (windGust / 50) * a0 + a1 / 2
-
- # Add new fcurves for each sway as well as the modifiers
- swayX = armOb.animation_data.action.fcurves.new('pose.bones["' + boneName + '"].rotation_euler', 0)
- swayY = armOb.animation_data.action.fcurves.new('pose.bones["' + boneName + '"].rotation_euler', 2)
-
- swayXMod1 = swayX.modifiers.new(type='FNGENERATOR')
- swayXMod2 = swayX.modifiers.new(type='FNGENERATOR')
-
- swayYMod1 = swayY.modifiers.new(type='FNGENERATOR')
- swayYMod2 = swayY.modifiers.new(type='FNGENERATOR')
-
- # Set the parameters for each modifier
- swayXMod1.amplitude = radians(a1) / numPoints
- swayXMod1.phase_offset = bxOffset
- swayXMod1.phase_multiplier = degrees(bMult)
-
- swayXMod2.amplitude = radians(a2) / numPoints
- swayXMod2.phase_offset = 0.7 * bxOffset
- swayXMod2.phase_multiplier = 0.7 * degrees(
- bMult) # This shouldn't have to be in degrees but it looks much better in animation
- swayXMod2.use_additive = True
-
- swayYMod1.amplitude = radians(a1) / numPoints
- swayYMod1.phase_offset = byOffset
- swayYMod1.phase_multiplier = degrees(
- bMult) # This shouldn't have to be in degrees but it looks much better in animation
-
- swayYMod2.amplitude = radians(a2) / numPoints
- swayYMod2.phase_offset = 0.7 * byOffset
- swayYMod2.phase_multiplier = 0.7 * degrees(
- bMult) # This shouldn't have to be in degrees but it looks much better in animation
- swayYMod2.use_additive = True
-
- # If there are leaves we need to assign vertices to their vertex groups
+ splineL = numPoints * ((s.bezier_points[0].co - s.bezier_points[1].co).length)
+ # Set the random phase difference of the animation
+ bxOffset = uniform(0, tau)
+ byOffset = uniform(0, tau)
+ # Set the phase multiplier for the spline
+ #bMult_r = (s.bezier_points[0].radius / max(splineL, 1e-6)) * (1 / 15) * (1 / frameRate)
+ #bMult = degrees(bMult_r) # This shouldn't have to be in degrees but it looks much better in animation
+ bMult = (1 / max(splineL ** .5, 1e-6)) * (1 / 4)
+ #print((1 / bMult) * tau) #print wavelength in frames
+
+ windFreq1 = bMult * animSpeed
+ windFreq2 = 0.7 * bMult * animSpeed
+ if loopFrames != 0:
+ bMult_l = 1 / (loopFrames / tau)
+ fRatio = max(1, round(windFreq1 / bMult_l))
+ fgRatio = max(1, round(windFreq2 / bMult_l))
+ windFreq1 = fRatio * bMult_l
+ windFreq2 = fgRatio * bMult_l
+
+ # For all the points in the curve (less the last) add a bone and name it by the spline it will affect
+ nx = 0
+ for n in range(0, numPoints, step):
+ oldBone = b
+ boneName = 'bone' + (str(i)).rjust(3, '0') + '.' + (str(n)).rjust(3, '0')
+ b = arm.edit_bones.new(boneName)
+ b.head = s.bezier_points[n].co
+ nx += step
+ nx = min(nx, numPoints)
+ b.tail = s.bezier_points[nx].co
+
+ b.head_radius = s.bezier_points[n].radius
+ b.tail_radius = s.bezier_points[n + 1].radius
+ b.envelope_distance = 0.001
+
+# # If there are leaves then we need a new vertex group so they will attach to the bone
+# if not leafAnim:
+# if (len(levelCount) > 1) and (i >= levelCount[-2]) and leafObj:
+# leafObj.vertex_groups.new(boneName)
+# elif (len(levelCount) == 1) and leafObj:
+# leafObj.vertex_groups.new(boneName)
+
+ # If this is first point of the spline then it must be parented to the level above it
+ if n == 0:
+ if parBone:
+ b.parent = arm.edit_bones[parBone]
+ # Otherwise, we need to attach it to the previous bone in the spline
+ else:
+ b.parent = oldBone
+ b.use_connect = True
+ # If there isn't a previous bone then it shouldn't be attached
+ if not oldBone:
+ b.use_connect = False
+
+ # Add the animation to the armature if required
+ if armAnim:
+ # Define all the required parameters of the wind sway by the dimension of the spline
+ #a0 = 4 * splineL * (1 - n / (numPoints + 1)) / max(s.bezier_points[n].radius, 1e-6)
+ a0 = 2 * (splineL / numPoints) * (1 - n / (numPoints + 1)) / max(s.bezier_points[n].radius, 1e-6)
+ a0 = a0 * min(step, numPoints)
+ #a0 = (splineL / numPoints) / max(s.bezier_points[n].radius, 1e-6)
+ a1 = (wind / 50) * a0
+ a2 = a1 * .65 #(windGust / 50) * a0 + a1 / 2
+
+ p = s.bezier_points[nx].co - s.bezier_points[n].co
+ p.normalize()
+ ag = (wind * gust / 50) * a0
+ a3 = -p[0] * ag
+ a4 = p[2] * ag
+
+ a1 = radians(a1)
+ a2 = radians(a2)
+ a3 = radians(a3)
+ a4 = radians(a4)
+
+ #wind bending
+ if loopFrames == 0:
+ swayFreq = gustF * (tau / fps) * frameRate #animSpeed # .075 # 0.02
+ else:
+ swayFreq = 1 / (loopFrames / tau)
+
+ # Prevent tree base from rotating
+ if (boneName == "bone000.000") or (boneName == "bone000.001"):
+ a1 = 0
+ a2 = 0
+ a3 = 0
+ a4 = 0
+
+ # Add new fcurves for each sway as well as the modifiers
+ swayX = armOb.animation_data.action.fcurves.new('pose.bones["' + boneName + '"].rotation_euler', 0)
+ swayY = armOb.animation_data.action.fcurves.new('pose.bones["' + boneName + '"].rotation_euler', 2)
+
+ swayXMod1 = swayX.modifiers.new(type='FNGENERATOR')
+ swayXMod2 = swayX.modifiers.new(type='FNGENERATOR')
+
+ swayYMod1 = swayY.modifiers.new(type='FNGENERATOR')
+ swayYMod2 = swayY.modifiers.new(type='FNGENERATOR')
+
+ # Set the parameters for each modifier
+ swayXMod1.amplitude = a1
+ swayXMod1.phase_offset = bxOffset
+ swayXMod1.phase_multiplier = windFreq1
+
+ swayXMod2.amplitude = a2
+ swayXMod2.phase_offset = 0.7 * bxOffset
+ swayXMod2.phase_multiplier = windFreq2
+ swayXMod2.use_additive = True
+
+ swayYMod1.amplitude = a1
+ swayYMod1.phase_offset = byOffset
+ swayYMod1.phase_multiplier = windFreq1
+
+ swayYMod2.amplitude = a2
+ swayYMod2.phase_offset = 0.7 * byOffset
+ swayYMod2.phase_multiplier = windFreq2
+ swayYMod2.use_additive = True
+
+ #wind bending
+ swayYMod3 = swayY.modifiers.new(type='FNGENERATOR')
+ swayYMod3.amplitude = a3
+ swayYMod3.phase_multiplier = swayFreq
+ swayYMod3.value_offset = .6 * a3
+ swayYMod3.use_additive = True
+
+ swayXMod3 = swayX.modifiers.new(type='FNGENERATOR')
+ swayXMod3.amplitude = a4
+ swayXMod3.phase_multiplier = swayFreq
+ swayXMod3.value_offset = .6 * a4
+ swayXMod3.use_additive = True
+
if leaves:
- offsetVal = 0
- leafVertSize = 6
- if leafShape == 'rect':
- leafVertSize = 4
- for i, cp in enumerate(childP):
- for v in leafMesh.vertices[leafVertSize * i:(leafVertSize * i + leafVertSize)]:
- leafObj.vertex_groups[cp.parBone].add([v.index], 1.0, 'ADD')
+ bonelist = [b.name for b in arm.edit_bones]
+ vertexGroups = OrderedDict()
+ for i, cp in enumerate(leafP):
+ # find leafs parent bone
+ leafParent = roundBone(cp.parBone, boneStep[armLevels])
+ idx = int(leafParent[4:-4])
+ while leafParent not in bonelist:
+ #find parent bone of parent bone
+ leafParent = splineToBone[idx]
+ idx = int(leafParent[4:-4])
+
+ if leafAnim:
+ bname = "leaf" + str(i)
+ b = arm.edit_bones.new(bname)
+ b.head = cp.co
+ b.tail = cp.co + Vector((0, 0, .02))
+ b.envelope_distance = 0.0
+ b.parent = arm.edit_bones[leafParent]
+
+ vertexGroups[bname] = [v.index for v in leafMesh.vertices[leafVertSize * i:(leafVertSize * i + leafVertSize)]]
+
+ if armAnim:
+ # Define all the required parameters of the wind sway by the dimension of the spline
+ a1 = wind * .25
+ a1 *= af1
+
+ bMult = (1 / animSpeed) * 6
+ bMult *= 1 / max(af2, .001)
+
+ ofstRand = af3
+ bxOffset = uniform(-ofstRand, ofstRand)
+ byOffset = uniform(-ofstRand, ofstRand)
+
+ # Add new fcurves for each sway as well as the modifiers
+ swayX = armOb.animation_data.action.fcurves.new('pose.bones["' + bname + '"].rotation_euler', 0)
+ swayY = armOb.animation_data.action.fcurves.new('pose.bones["' + bname + '"].rotation_euler', 2)
+
+ # Add keyframe so noise works
+ swayX.keyframe_points.add()
+ swayY.keyframe_points.add()
+ swayX.keyframe_points[0].co = (0, 0)
+ swayY.keyframe_points[0].co = (0, 0)
+
+ # Add noise modifiers
+ swayXMod = swayX.modifiers.new(type='NOISE')
+ swayYMod = swayY.modifiers.new(type='NOISE')
+
+ if loopFrames != 0:
+ swayXMod.use_restricted_range = True
+ swayXMod.frame_end = loopFrames
+ swayXMod.blend_in = 4
+ swayXMod.blend_out = 4
+ swayYMod.use_restricted_range = True
+ swayYMod.frame_end = loopFrames
+ swayYMod.blend_in = 4
+ swayYMod.blend_out = 4
+
+ swayXMod.scale = bMult
+ swayXMod.strength = a1
+ swayXMod.offset = bxOffset
+
+ swayYMod.scale = bMult
+ swayYMod.strength = a1
+ swayYMod.offset = byOffset
+
+ else:
+ if leafParent not in vertexGroups:
+ vertexGroups[leafParent] = []
+ vertexGroups[leafParent].extend([v.index for v in leafMesh.vertices[leafVertSize * i:(leafVertSize * i + leafVertSize)]])
+
+ for group in vertexGroups:
+ leafObj.vertex_groups.new(group)
+ leafObj.vertex_groups[group].add(vertexGroups[group], 1.0, 'ADD')
# Now we need the rotation mode to be 'XYZ' to ensure correct rotation
bpy.ops.object.mode_set(mode='OBJECT')
@@ -534,31 +914,131 @@ def create_armature(armAnim, childP, cu, frameRate, leafMesh, leafObj, leafShape
treeOb.parent = armOb
-def kickstart_trunk(addstem, branches, cu, curve, curveRes, curveV, length, lengthV, ratio, resU, scale0, scaleV0,
- scaleVal, taper, vertAtt):
- vertAtt = 0.0
+def kickstart_trunk(addstem, levels, leaves, branches, cu, curve, curveRes, curveV, attractUp, length, lengthV, ratio, ratioPower, resU, scale0, scaleV0,
+ scaleVal, taper, minRadius, rootFlare):
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] + uniform(-lengthV[0], lengthV[0]))
- childStems = branches[1]
- startRad = branchL * ratio * (scale0 + uniform(-scaleV0, scaleV0))
- endRad = startRad * (1 - taper[0])
- newPoint.radius = startRad
+ # (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
addstem(
- stemSpline(newSpline, curve[0] / curveRes[0], curveV[0] / curveRes[0], 0, curveRes[0], branchL / curveRes[0],
- childStems, startRad, endRad, 0))
- return vertAtt
+ 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)
+ # Store the old rotation to allow new stems to be rotated away from the previous one.
+ oldRotate = 0
-def fabricate_stems(addsplinetobone, addstem, baseSize, branches, childP, cu, curve, curveBack, curveRes, curveV,
- downAngle, downAngleV, leafDist, leaves, length, lengthV, levels, n, oldRotate, ratioPower, resU,
- rotate, rotateV, scaleVal, shape, storeN, taper, vertAtt):
- for p in childP:
+ #use fancy child point selection / rotation
+ 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)
+
+ #account for down angle
+ 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:
+ 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
@@ -566,71 +1046,118 @@ def fabricate_stems(addsplinetobone, addstem, baseSize, branches, childP, cu, cu
newPoint.co = p.co
tempPos = zAxis.copy()
# If the -ve flag for downAngle is used we need a special formula to find it
- if downAngleV[n] < 0.0:
- downV = downAngleV[n] * (
- 1 - 2 * shapeRatio(0, (p.lengthPar - p.offset) / (p.lengthPar - baseSize * scaleVal)))
- random()
- # Otherwise just find a random value
+ 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])
else:
- downV = uniform(-downAngleV[n], downAngleV[n])
- downRotMat = Matrix.Rotation(downAngle[n] + downV, 3, 'X')
- tempPos.rotate(downRotMat)
+ 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] + uniform(-rotateV[n], rotateV[n]), oldRotate)
+ oldRotate = -copysign(rotate[n], oldRotate)
# Otherwise just generate a random number in the specified range
else:
- oldRotate += rotate[n] + uniform(-rotateV[n], rotateV[n])
+ 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
- rotMat = Matrix.Rotation(oldRotate, 3, 'Z')
+ tempPos.rotate(downRotMat)
tempPos.rotate(rotMat)
- tempPos.rotate(p.quat)
+
+ #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)
+
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
+ 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):
- # If this is the last level before leaves then we need to generate the child points differently
- branchL = (length[n] + uniform(-lengthV[n], lengthV[n])) * (p.lengthPar - 0.6 * p.offset)
if leaves < 0:
childStems = False
else:
- childStems = leaves * shapeRatio(leafDist, p.offset / p.lengthPar)
- elif n == 1:
- # If this is the first level of branching then upward attraction has no effect
- # and a special formula is used to find branch length and the number of child stems.
- # This is also here that the shape is used.
- vertAtt = 0.0
- lMax = length[1] + uniform(-lengthV[1], lengthV[1])
- lMax += copysign(1e-6, lMax) # Move away from zero to avoid div by zero
- branchL = p.lengthPar * lMax * shapeRatio(shape,
- (p.lengthPar - p.offset) / (p.lengthPar - baseSize * scaleVal))
- childStems = branches[2] * (0.2 + 0.8 * (branchL / p.lengthPar) / lMax)
- else:
- branchL = (length[n] + uniform(-lengthV[n], lengthV[n])) * (p.lengthPar - 0.6 * p.offset)
- childStems = branches[min(3, n + 1)] * (1.0 - 0.5 * p.offset / p.lengthPar)
+ 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))
- branchL = max(branchL, 0.0)
+
# Determine the starting and ending radii of the stem using the tapering of the stem
- startRad = min(p.radiusPar[0] * ((branchL / p.lengthPar) ** ratioPower), p.radiusPar[1])
- endRad = startRad * (1 - taper[n])
+ 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)
newPoint.radius = startRad
- # If curveBack is used then the curviness of the stem is different for the first half
- if curveBack[n] == 0:
- curveVal = curve[n] / curveRes[n]
- else:
- curveVal = 2 * curve[n] / curveRes[n]
+
+ # 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, curveV[n] / curveRes[n], 0, curveRes[n], branchL / curveRes[n], childStems,
- startRad, endRad, len(cu.splines) - 1))
- addsplinetobone(p.parBone)
- return vertAtt
+ 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, pruneWidthPeak, randState, ratio, scaleVal, segSplits,
- splineToBone, splitAngle, splitAngleV, st, startPrune, vertAtt):
+ 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)
@@ -661,36 +1188,77 @@ def perform_pruning(baseSize, baseSplits, childP, cu, currentMax, currentMin, cu
# 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))
+ # 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
+
if k == 0:
numSplit = 0
+ elif (n == 0) and (k < ((curveRes[n]-1) * splitHeight)) and (k != 1):
+ numSplit = 0
elif (k == 1) and (n == 0):
numSplit = baseSplits
+ elif (n == 0) and (k == int((curveRes[n]-1) * splitHeight) + 1) and (splitVal > 0): #allways split at splitHeight
+ numSplit = 1
else:
- numSplit = splits(segSplits[n])
- if (k == int(curveRes[n] / 2)) and (curveBack[n] != 0):
- spl.curvAdd(-2 * curve[n] / curveRes[n] + 2 * curveBack[n] / curveRes[n])
- growSpline(spl, numSplit, splitAngle[n], splitAngleV[n], splineList, vertAtt, handles,
- splineToBone) # Add proper refs for radius and attractUp
+ 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 - baseSize, 1e-6))
+ ratio = (scaleVal - s.spline.bezier_points[-1].co.z) / (scaleVal * max(1 - pruneBase, 1e-6))
# Don't think this if part is needed
- if (n == 0) and (s.spline.bezier_points[-1].co.z < baseSize * scaleVal):
+ if (n == 0) and (s.spline.bezier_points[-1].co.z < pruneBase * scaleVal):
insideBool = True # Init to avoid UnboundLocalError later
else:
insideBool = (
- (coordMag / scaleVal) < pruneWidth * shapeRatio(8, ratio, pruneWidthPeak, prunePowerHigh,
+ (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:
@@ -705,23 +1273,42 @@ def perform_pruning(baseSize, baseSplits, childP, cu, currentMax, currentMin, cu
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:
- tVals = findChildPoints(splineList, st.children)
+ 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:
if not st.children:
- tVals = [0.9]
- # If this is the trunk then we need to remove some of the points because of baseSize
+ 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
if n == 0:
- trimNum = int(baseSize * (len(tVals) + 1))
- tVals = tVals[trimNum:]
+ 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])
for s in splineList:
- #print(str(n)+'level: ',s.segMax*s.segL)
- childP.extend(interpStem(s, tVals, s.segMax * s.segL, s.radS))
+ #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
@@ -730,17 +1317,55 @@ def perform_pruning(baseSize, baseSplits, childP, cu, currentMax, currentMin, cu
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
+
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)#
@@ -748,15 +1373,30 @@ def addTree(props):
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
baseSize = props.baseSize
+ baseSize_s = props.baseSize_s
+ splitHeight = props.splitHeight
+ splitBias = props.splitBias
ratio = props.ratio
+ minRadius = props.minRadius
+ closeTip = props.closeTip
+ rootFlare = props.rootFlare
+ autoTaper = props.autoTaper
taper = props.taper#
+ radiusTweak = props.radiusTweak
ratioPower = props.ratioPower#
downAngle = toRad(props.downAngle)#
downAngleV = toRad(props.downAngleV)#
@@ -766,26 +1406,64 @@ def addTree(props):
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
leafShape = props.leafShape
+ leafDupliObj = props.leafDupliObj
bend = props.bend#
+ leafangle = props.leafangle
+ horzLeaves = props.horzLeaves
leafDist = int(props.leafDist)#
bevelRes = props.bevelRes#
resU = props.resU#
+
useArm = props.useArm
-
- frameRate = props.frameRate
- windSpeed = props.windSpeed
- windGust = props.windGust
+ previewArm = props.previewArm
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]
+
+ #taper
+ if autoTaper:
+ taper = findtaper(length, taper, shape, shapeS, levels, customShape)
+ #pLevels = branches[0]
+ #taper = findtaper(length, taper, shape, shapeS, pLevels, customShape)
+
leafObj = None
-
+
# Some effects can be turned ON and OFF, the necessary variables are changed here
if not props.bevel:
bevelDepth = 0.0
@@ -805,89 +1483,95 @@ def addTree(props):
for ob in bpy.data.objects:
ob.select = False
- childP = []
- stemList = []
-
# Initialise the tree object and curve and adjust the settings
- cu = bpy.data.curves.new('tree','CURVE')
- treeOb = bpy.data.objects.new('tree',cu)
+ cu = bpy.data.curves.new('tree', 'CURVE')
+ treeOb = bpy.data.objects.new('tree', cu)
bpy.context.scene.objects.link(treeOb)
-
- treeOb.location=bpy.context.scene.cursor_location
+
+# 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
# Fix the scale of the tree now
- scaleVal = scale + uniform(-scaleV,scaleV)
+ 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)
+ 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)
+ 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()
newPoint = newSpline.bezier_points[-1]
ratioVal = (c+1)/(enNum)
- zVal = scaleVal - scaleVal*(1-baseSize)*ratioVal
- newPoint.co = Vector((scaleVal*pruneWidth*shapeRatio(8,ratioVal,pruneWidthPeak,prunePowerHigh,prunePowerLow),0,zVal))
- (newPoint.handle_right_type,newPoint.handle_left_type) = (enHandle,enHandle)
+ 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)
+ 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()
newPoint = newSpline.bezier_points[-1]
ratioVal = (c+1)/(enNum)
- zVal = scaleVal - scaleVal*(1-baseSize)*ratioVal
- newPoint.co = Vector((0,scaleVal*pruneWidth*shapeRatio(8,ratioVal,pruneWidthPeak,prunePowerHigh,prunePowerLow),zVal))
- (newPoint.handle_right_type,newPoint.handle_left_type) = (enHandle,enHandle)
+ 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)
- leafVerts = []
- leafFaces = []
- levelCount = []
+ childP = []
+ stemList = []
+
+ levelCount = []
splineToBone = deque([''])
addsplinetobone = splineToBone.append
-
- leafMesh = None # in case we aren't creating leaves, we'll still have the variable
- # Each of the levels needed by the user we grow all the splines
+ # 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
- n = min(3,n)
- vertAtt = attractUp
+ n = min(3, n)
splitError = 0.0
+
+ #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:
- vertAtt = kickstart_trunk(addstem, branches, cu, curve, curveRes, curveV, length, lengthV, ratio, resU,
- scale0, scaleV0, scaleVal, taper, vertAtt)
+ 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:
- # Store the old rotation to allow new stems to be rotated away from the previous one.
- oldRotate = 0
# For each of the points defined in the list of stem starting points we need to grow a stem.
- vertAtt = fabricate_stems(addsplinetobone, addstem, baseSize, branches, childP, cu, curve, curveBack,
- curveRes, curveV, downAngle, downAngleV, leafDist, leaves, length, lengthV,
- levels, n, oldRotate, ratioPower, resU, rotate, rotateV, scaleVal, shape, storeN,
- taper, vertAtt)
+ 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
@@ -917,77 +1601,269 @@ def addTree(props):
handles, n, oldMax, orginalSplineToBone, originalCo, originalCurv,
originalCurvV, originalHandleL, originalHandleR, originalLength,
originalSeg, prune, prunePowerHigh, prunePowerLow, pruneRatio,
- pruneWidth, pruneWidthPeak, randState, ratio, scaleVal, segSplits,
- splineToBone, splitAngle, splitAngleV, st, startPrune, vertAtt)
+ 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)
levelCount.append(len(cu.splines))
- # If we need to add leaves, we do it here
- if (storeN == levels-1) and 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 = -rotate[n]/2
- for g in range(abs(leaves)):
- (vertTemp,faceTemp,oldRot) = genLeafMesh(leafScale,leafScaleX,cp.co,cp.quat,len(leafVerts),downAngle[n],downAngleV[n],rotate[n],rotateV[n],oldRot,bend,leaves, leafShape)
- leafVerts.extend(vertTemp)
- leafFaces.extend(faceTemp)
- # Otherwise just add the leaves like splines.
- else:
- (vertTemp,faceTemp,oldRot) = genLeafMesh(leafScale,leafScaleX,cp.co,cp.quat,len(leafVerts),downAngle[n],downAngleV[n],rotate[n],rotateV[n],oldRot,bend,leaves, leafShape)
+
+ # 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)
- # 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)
-
- if leafShape == 'rect':
- leafMesh.uv_textures.new("leafUV")
- uvlayer = leafMesh.uv_layers.active.data
-
- for i in range(0, len(leafFaces)):
- uvlayer[i*4 + 0].uv = Vector((1, 0))
- uvlayer[i*4 + 1].uv = Vector((1, 1))
- uvlayer[i*4 + 2].uv = Vector((1 - leafScaleX, 1))
- uvlayer[i*4 + 3].uv = Vector((1 - leafScaleX, 0))
-
- leafMesh.validate()
-
-# This can be used if we need particle leaves
-# if (storeN == levels-1) and leaves:
-# normalList = []
-# oldRot = 0.0
-# n = min(3,n+1)
-# oldRot = 0.0
-# # For each of the child points we add leaves
-# for cp in childP:
-# # Here we make the new "sprouting" stems diverge from the current direction
-# dirVec = zAxis.copy()
-# oldRot += rotate[n]+uniform(-rotateV[n],rotateV[n])
-# downRotMat = Matrix.Rotation(downAngle[n]+uniform(-downAngleV[n],downAngleV[n]),3,'X')
-# rotMat = Matrix.Rotation(oldRot,3,'Z')
-# dirVec.rotate(downRotMat)
-# dirVec.rotate(rotMat)
-# dirVec.rotate(cp.quat)
-# normalList.extend([dirVec.x,dirVec.y,dirVec.z])
-# leafVerts.append(cp.co)
-# # Create the leaf mesh and object, add geometry using from_pydata, edges are currently added by validating the mesh which isn't great
-# edgeList = [(a,a+1) for a in range(len(childP)-1)]
-# 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,edgeList,())
-# leafMesh.vertices.foreach_set('normal',normalList)
-
- # If we need and armature we add it
+ 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
if useArm:
# Create the armature and objects
- create_armature(armAnim, childP, cu, frameRate, leafMesh, leafObj, leafShape, leaves, levelCount, splineToBone,
- treeOb, windGust, windSpeed)
+ 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)
+
#print(time.time()-startTime)
+
+
+ #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)