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
parentfe34f82e7059bb6b89dfc88b55f030111f2d431f (diff)
Update sapling tree gen: T46559 major rewrite
Diffstat (limited to 'add_curve_sapling')
-rw-r--r--add_curve_sapling/__init__.py613
-rw-r--r--add_curve_sapling/presets/Callistemon.py1
-rw-r--r--add_curve_sapling/presets/Douglas Fir.py1
-rw-r--r--add_curve_sapling/presets/Japanese Maple.py1
-rw-r--r--add_curve_sapling/presets/Small Maple.py1
-rw-r--r--add_curve_sapling/presets/Small Pine.py1
-rw-r--r--add_curve_sapling/presets/Weeping Willow.py1
-rw-r--r--add_curve_sapling/presets/White Birch.py1
-rw-r--r--add_curve_sapling/presets/black_tupelo.py1
-rw-r--r--add_curve_sapling/presets/ca_black_oak.py1
-rw-r--r--add_curve_sapling/presets/quaking_aspen.py2
-rw-r--r--add_curve_sapling/utils.py1774
12 files changed, 1814 insertions, 584 deletions
diff --git a/add_curve_sapling/__init__.py b/add_curve_sapling/__init__.py
index fa1b3267..4360fd37 100644
--- a/add_curve_sapling/__init__.py
+++ b/add_curve_sapling/__init__.py
@@ -17,20 +17,15 @@
#======================= END GPL LICENSE BLOCK ========================
bl_info = {
- "name": "Sapling",
- "author": "Andrew Hale (TrumanBlending)",
- "version": (0, 2, 6),
- "blender": (2, 73, 0),
+ "name": "Sapling Tree Gen",
+ "author": "Andrew Hale (TrumanBlending), Aaron Buchler",
+ "version": (0, 3, 2),
+ "blender": (2, 77, 0),
"location": "View3D > Add > Curve",
"description": ("Adds a parametric tree. The method is presented by "
"Jason Weber & Joseph Penn in their paper 'Creation and Rendering of "
"Realistic Trees'."),
- "warning": "",
- "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Curve/Sapling_Tree",
- "category": "Add Curve",
-}
-
+ "category": "Add Curve"}
if "bpy" in locals():
import importlib
@@ -41,8 +36,10 @@ else:
import bpy
import time
import os
+import ast
+
+#import cProfile
-#from utils import *
from mathutils import *
from math import pi, sin, degrees, radians, atan2, copysign
from random import random, uniform, seed, choice, getstate, setstate
@@ -50,7 +47,6 @@ from bpy.props import *
from add_curve_sapling.utils import *
-#global splitError
useSet = False
shapeList = [('0', 'Conical (0)', 'Shape = 0'),
@@ -62,28 +58,74 @@ shapeList = [('0', 'Conical (0)', 'Shape = 0'),
('6', 'Inverse Conical (6)', 'Shape = 6'),
('7', 'Tend Flame (7)', 'Shape = 7')]
+shapeList3 = [('0', 'Conical', ''),
+ ('6', 'Inverse Conical', ''),
+ ('1', 'Spherical', ''),
+ ('2', 'Hemispherical', ''),
+ ('3', 'Cylindrical', ''),
+ ('4', 'Tapered Cylindrical', ''),
+ ('10', 'Inverse Tapered Cylindrical', ''),
+ ('5', 'Flame', ''),
+ ('7', 'Tend Flame', ''),
+ ('8', 'Custom Shape', '')]
+
+shapeList4 = [('0', 'Conical', ''),
+ ('6', 'Inverse Conical', ''),
+ ('1', 'Spherical', ''),
+ ('2', 'Hemispherical', ''),
+ ('3', 'Cylindrical', ''),
+ ('4', 'Tapered Cylindrical', ''),
+ ('10', 'Inverse Tapered Cylindrical', ''),
+ ('5', 'Flame', ''),
+ ('7', 'Tend Flame', '')]
+
handleList = [('0', 'Auto', 'Auto'),
('1', 'Vector', 'Vector')]
settings = [('0', 'Geometry', 'Geometry'),
- ('1', 'Branch Splitting', 'Branch Splitting'),
- ('2', 'Branch Growth', 'Branch Growth'),
- ('3', 'Pruning', 'Pruning'),
- ('4', 'Leaves', 'Leaves'),
- ('5', 'Armature', 'Armature')]
-
+ ('1', 'Branch Radius', 'Branch Radius'),
+ ('2', 'Branch Splitting', 'Branch Splitting'),
+ ('3', 'Branch Growth', 'Branch Growth'),
+ ('4', 'Pruning', 'Pruning'),
+ ('5', 'Leaves', 'Leaves'),
+ ('6', 'Armature', 'Armature'),
+ ('7', 'Animation', 'Animation')]
+
+branchmodes = [("original", "Original", "rotate around each branch"),
+ ("rotate", "Rotate", "evenly distribute branches to point outward from center of tree"),
+ ("random", "Random", "choose random point")]
def getPresetpath():
"""Support user defined scripts directory
Find the first ocurrence of add_curve_sapling/presets in possible script paths
and return it as preset path"""
- presetpath = ""
- for p in bpy.utils.script_paths():
- presetpath = os.path.join(p, 'addons', 'add_curve_sapling', 'presets')
- if os.path.exists(presetpath):
- break
- return presetpath
-
+ #presetpath = ""
+ #for p in bpy.utils.script_paths():
+ # presetpath = os.path.join(p, 'addons', 'add_curve_sapling_3', 'presets')
+ # if os.path.exists(presetpath):
+ # break
+ #return presetpath
+
+ # why not just do this
+ script_file = os.path.realpath(__file__)
+ directory = os.path.dirname(script_file)
+ directory = os.path.join(directory, "presets")
+ return directory
+
+def getPresetpaths():
+ """Return paths for both local and user preset folders"""
+ userDir = os.path.join(bpy.utils.script_path_user(), 'presets', 'operator', 'add_curve_sapling')
+
+ if os.path.isdir(userDir):
+ pass
+ else:
+ os.makedirs(userDir)
+
+ script_file = os.path.realpath(__file__)
+ directory = os.path.dirname(script_file)
+ localDir = os.path.join(directory, "presets")
+
+ return (localDir, userDir)
class ExportData(bpy.types.Operator):
"""This operator handles writing presets to file"""
@@ -94,24 +136,46 @@ class ExportData(bpy.types.Operator):
def execute(self, context):
# Unpack some data from the input
- data, filename = eval(self.data)
- try:
- # Check whether the file exists by trying to open it.
- f = open(os.path.join(getPresetpath(), filename + '.py'), 'r')
- f.close()
- # If it exists then report an error
- self.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
+ data, filename, overwrite = eval(self.data)
+
+# try:
+# # Check whether the file exists by trying to open it.
+# f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'r')
+# f.close()
+# # If it exists then report an error
+# self.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
+# return {'CANCELLED'}
+# except IOError:
+# if data:
+# # If it doesn't exist, create the file with the required data
+# f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'w')
+# f.write(data)
+# f.close()
+# return {'FINISHED'}
+# else:
+# return {'CANCELLED'}
+
+ fpath1 = os.path.join(getPresetpaths()[0], filename + '.py')
+ fpath2 = os.path.join(getPresetpaths()[1], filename + '.py')
+
+ if os.path.exists(fpath1):
+ # If it exists in built-in presets then report an error
+ self.report({'ERROR_INVALID_INPUT'}, 'Can\'t have same name as built-in preset')
return {'CANCELLED'}
- except IOError:
+ elif (not os.path.exists(fpath2)) or (os.path.exists(fpath2) and overwrite):
+ #if (it does not exist) or (exists and overwrite) then write file
if data:
# If it doesn't exist, create the file with the required data
- f = open(os.path.join(getPresetpath(), filename + '.py'), 'w')
+ f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'w')
f.write(data)
f.close()
return {'FINISHED'}
else:
return {'CANCELLED'}
-
+ else:
+ # If it exists then report an error
+ self.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
+ return {'CANCELLED'}
class ImportData(bpy.types.Operator):
"""This operator handles importing existing presets"""
@@ -124,40 +188,78 @@ class ImportData(bpy.types.Operator):
# Make sure the operator knows about the global variables
global settings, useSet
# Read the preset data into the global settings
- f = open(os.path.join(getPresetpath(), self.filename), 'r')
+ try:
+ f = open(os.path.join(getPresetpaths()[0], self.filename), 'r')
+ except (FileNotFoundError, IOError):
+ f = open(os.path.join(getPresetpaths()[1], self.filename), 'r')
settings = f.readline()
f.close()
#print(settings)
- settings = eval(settings)
+ settings = ast.literal_eval(settings)
+
+ #use old attractup
+ if type(settings['attractUp']) == float:
+ atr = settings['attractUp']
+ settings['attractUp'] = [0, 0, atr, atr]
+
+ #use old leaf rotations
+ if 'leafDownAngle' not in settings:
+ l = settings['levels']
+ settings['leafDownAngle'] = settings['downAngle'][min(l, 3)]
+ settings['leafDownAngleV'] = settings['downAngleV'][min(l, 3)]
+ settings['leafRotate'] = settings['rotate'][min(l, 3)]
+ settings['leafRotateV'] = settings['rotateV'][min(l, 3)]
+
+ #zero leaf bend
+ settings['bend'] = 0
+
# Set the flag to use the settings
useSet = True
return {'FINISHED'}
-
class PresetMenu(bpy.types.Menu):
- """Create the preset menu by finding all preset files """ \
- """in the preset directory"""
+ """Create the preset menu by finding all preset files
+ in the preset directory"""
bl_idname = "sapling.presetmenu"
bl_label = "Presets"
def draw(self, context):
# Get all the sapling presets
- presets = [a for a in os.listdir(getPresetpath()) if a[-3:] == '.py']
+ presets = [a for a in os.listdir(getPresetpaths()[0]) if a[-3:] == '.py']
+ presets.extend([a for a in os.listdir(getPresetpaths()[1]) if a[-3:] == '.py'])
layout = self.layout
# Append all to the menu
for p in presets:
layout.operator("sapling.importdata", text=p[:-3]).filename = p
-
class AddTree(bpy.types.Operator):
bl_idname = "curve.tree_add"
bl_label = "Sapling: Add Tree"
bl_options = {'REGISTER', 'UNDO'}
+ def objectList(self, context):
+ objects = []
+ bObjects = bpy.data.objects
+# try:
+# bObjects = bpy.data.objects
+# except AttributeError:
+# pass
+# else:
+ for obj in bObjects:
+ if (obj.type in ['MESH', 'CURVE', 'SURFACE']) and (obj.name not in ['tree', 'leaves']):
+ objects.append((obj.name, obj.name, ""))
+
+ return objects
def update_tree(self, context):
self.do_update = True
+ def update_leaves(self, context):
+ if self.showLeaves:
+ self.do_update = True
+ else:
+ self.do_update = False
+
def no_update_tree(self, context):
self.do_update = False
@@ -189,20 +291,27 @@ class AddTree(bpy.types.Operator):
max=1,
default=0, update=update_tree)
levels = IntProperty(name='Levels',
- description='Number of recursive branches (Levels, note that last level is also used for generating leaves)',
+ description='Number of recursive branches (Levels)',
min=1,
max=6,
+ soft_max=4,
default=3, update=update_tree)
length = FloatVectorProperty(name='Length',
description='The relative lengths of each branch level (nLength)',
- min=1e-6,
+ min=0.000001,
default=[1, 0.3, 0.6, 0.45],
size=4, update=update_tree)
lengthV = FloatVectorProperty(name='Length Variation',
description='The relative length variations of each level (nLengthV)',
min=0.0,
+ max=1.0,
default=[0, 0, 0, 0],
size=4, update=update_tree)
+ taperCrown = FloatProperty(name='Taper Crown',
+ description='Shorten trunk splits toward outside of tree',
+ min=0.0,
+ soft_max=1.0,
+ default=0, update=update_tree)
branches = IntVectorProperty(name='Branches',
description='The number of branches grown at each level (nBranches)',
min=0,
@@ -232,8 +341,16 @@ class AddTree(bpy.types.Operator):
segSplits = FloatVectorProperty(name='Segment Splits',
description='Number of splits per segment (nSegSplits)',
min=0,
+ soft_max=3,
default=[0, 0, 0, 0],
size=4, update=update_tree)
+ splitByLen = BoolProperty(name='Split relative to length',
+ description='Split proportional to branch length',
+ default=False, update=update_tree)
+ rMode = EnumProperty(name="", #"Branching Mode"
+ description='Branching and Rotation Mode',
+ items=branchmodes,
+ default="rotate", update=update_tree)
splitAngle = FloatVectorProperty(name='Split Angle',
description='Angle of branch splitting (nSplitAngle)',
default=[0, 0, 0, 0],
@@ -244,33 +361,94 @@ class AddTree(bpy.types.Operator):
size=4, update=update_tree)
scale = FloatProperty(name='Scale',
description='The tree scale (Scale)',
- min=0.001,
+ min=0.0,
default=13.0, update=update_tree)
scaleV = FloatProperty(name='Scale Variation',
description='The variation in the tree scale (ScaleV)',
default=3.0, update=update_tree)
- attractUp = FloatProperty(name='Vertical Attraction',
+ attractUp = FloatVectorProperty(name='Vertical Attraction',
description='Branch upward attraction',
- default=0.0, update=update_tree)
+ default=[0, 0, 0, 0],
+ size=4, update=update_tree)
+ attractOut = FloatVectorProperty(name='Outward Attraction',
+ description='Branch outward attraction',
+ default=[0, 0, 0, 0],
+ min=0.0,
+ max=1.0,
+ size=4, update=update_tree)
shape = EnumProperty(name='Shape',
- description='The overall shape of the tree (Shape) - WARNING: at least three "Levels" of branching are needed',
- items=shapeList,
+ description='The overall shape of the tree (Shape)',
+ items=shapeList3,
default='7', update=update_tree)
- baseSize = FloatProperty(name='Base Size',
- description='Fraction of tree height with no branches (BaseSize)',
- min=0.001,
+ shapeS = EnumProperty(name='Secondary Branches Shape',
+ description='The shape of secondary splits',
+ items=shapeList4,
+ default='4', update=update_tree)
+ customShape = FloatVectorProperty(name='Custom Shape',
+ description='custom shape branch length at (Base, Middle, Middle Position, Top)',
+ size=4,
+ min=.01,
+ max=1,
+ default=[.5, 1.0, .3, .5], update=update_tree)
+ branchDist = FloatProperty(name='Branch Distribution',
+ description='Adjust branch spacing to put more branches at the top or bottom of the tree',
+ min=0.1,
+ soft_max=10,
+ default=1.0, update=update_tree)
+ nrings = IntProperty(name='Branch Rings',
+ description='grow branches in rings',
+ min=0,
+ default=0, update=update_tree)
+ baseSize = FloatProperty(name='Trunk Height',
+ description='Fraction of tree height with no branches (Base Size)',
+ min=0.0,
max=1.0,
default=0.4, update=update_tree)
+ baseSize_s = FloatProperty(name='Secondary Base Size',
+ description='Factor to decrease base size for each level',
+ min=0.0,
+ max=1.0,
+ default=0.25, update=update_tree)
+ splitHeight = FloatProperty(name='Split Height',
+ description='Fraction of tree height with no splits',
+ min=0.0,
+ max=1.0,
+ default=0.2, update=update_tree)
+ splitBias = FloatProperty(name='splitBias',
+ description='Put more splits at the top or bottom of the tree',
+ soft_min=-2.0,
+ soft_max=2.0,
+ default=0.0, update=update_tree)
ratio = FloatProperty(name='Ratio',
description='Base radius size (Ratio)',
min=0.0,
default=0.015, update=update_tree)
+ minRadius = FloatProperty(name='Minimum Radius',
+ description='Minimum branch Radius',
+ min=0.0,
+ default=0.0, update=update_tree)
+ closeTip = BoolProperty(name='Close Tip',
+ description='Set radius at branch tips to zero',
+ default=False, update=update_tree)
+ rootFlare = FloatProperty(name='Root Flare',
+ description='Root radius factor',
+ min=1.0,
+ default=1.0, update=update_tree)
+ autoTaper = BoolProperty(name='Auto Taper',
+ description='Calculate taper automaticly based on branch lengths',
+ default=True, update=update_tree)
taper = FloatVectorProperty(name='Taper',
description='The fraction of tapering on each branch (nTaper)',
min=0.0,
max=1.0,
default=[1, 1, 1, 1],
size=4, update=update_tree)
+ radiusTweak = FloatVectorProperty(name='Tweak Radius',
+ description='multiply radius by this factor',
+ min=0.0,
+ max=1.0,
+ default=[1, 1, 1, 1],
+ size=4, update=update_tree)
ratioPower = FloatProperty(name='Branch Radius Ratio',
description=('Power which defines the radius of a branch compared to '
'the radius of the branch it grew from (RatioPower)'),
@@ -282,13 +460,17 @@ class AddTree(bpy.types.Operator):
default=[90, 60, 45, 45],
size=4, update=update_tree)
downAngleV = FloatVectorProperty(name='Down Angle Variation',
- description='Variation in the down angle (nDownAngleV)',
+ description='Angle to decrease Down Angle by towards end of parent branch (negative values add random variation)',
default=[0, -50, 10, 10],
size=4, update=update_tree)
+ useOldDownAngle = BoolProperty(name='Use old down angle variation',
+ default=False, update=update_tree)
+ useParentAngle = BoolProperty(name = 'Use parent angle',
+ description = '(first level) Rotate branch to match parent branch',
+ default=True, update=update_tree)
rotate = FloatVectorProperty(name='Rotate Angle',
- description=('The angle of a new branch around the one it grew from '
- '(nRotate)'),
- default=[140, 140, 140, 77],
+ description='The angle of a new branch around the one it grew from (negative values rotate opposite from the previous)',
+ default=[137.5, 137.5, 137.5, 137.5],
size=4, update=update_tree)
rotateV = FloatVectorProperty(name='Rotate Angle Variation',
description='Variation in the rotate angle (nRotateV)',
@@ -300,11 +482,18 @@ class AddTree(bpy.types.Operator):
default=1.0, update=update_tree)
scaleV0 = FloatProperty(name='Radius Scale Variation',
description='Variation in the radius scale (0ScaleV)',
+ min=0.0,
+ max=1.0,
default=0.2, update=update_tree)
pruneWidth = FloatProperty(name='Prune Width',
description='The width of the envelope (PruneWidth)',
min=0.0,
default=0.4, update=update_tree)
+ pruneBase = FloatProperty(name='Prune Base Height',
+ description='The height of the base of the envelope, bound by trunk height',
+ min=0.0,
+ max=1.0,
+ default=0.3, update=update_tree)
pruneWidthPeak = FloatProperty(name='Prune Width Peak',
description=('Fraction of envelope height where the maximum width '
'occurs (PruneWidthPeak)'),
@@ -323,36 +512,71 @@ class AddTree(bpy.types.Operator):
min=0.0,
max=1.0,
default=1.0, update=update_tree)
- leaves = FloatProperty(name='Leaves',
- description='Maximum number of leaves per branch (Leaves)',
- min=0,
- max=50,
+ leaves = IntProperty(name='Leaves',
+ description='Maximum number of leaves per branch (negative values grow leaves from branch tip (palmate compound leaves))',
default=25, update=update_tree)
+
+ leafDownAngle = FloatProperty(name='Leaf Down Angle',
+ description='The angle between a new leaf and the branch it grew from',
+ default=45, update=update_leaves)
+ leafDownAngleV = FloatProperty(name='Leaf Down Angle Variation',
+ description='Angle to decrease Down Angle by towards end of parent branch (negative values add random variation)',
+ default=10, update=update_tree)
+ leafRotate = FloatProperty(name='Leaf Rotate Angle',
+ description='The angle of a new leaf around the one it grew from (negative values rotate opposite from previous)',
+ default=137.5, update=update_tree)
+ leafRotateV = FloatProperty(name='Leaf Rotate Angle Variation',
+ description='Variation in the rotate angle',
+ default=0.0, update=update_leaves)
+
leafScale = FloatProperty(name='Leaf Scale',
description='The scaling applied to the whole leaf (LeafScale)',
min=0.0,
- default=0.17, update=update_tree)
+ default=0.17, update=update_leaves)
leafScaleX = FloatProperty(name='Leaf Scale X',
description=('The scaling applied to the x direction of the leaf '
'(LeafScaleX)'),
min=0.0,
- default=1.0, update=update_tree)
- leafShape = leafDist = EnumProperty(name='Leaf Shape',
- description='The shape of the leaves, rectangular are UV mapped',
- items=(('hex', 'Hexagonal', '0'), ('rect', 'Rectangular', '1')),
- default='hex', update=update_tree)
- bend = FloatProperty(name='Leaf Bend',
- description='The proportion of bending applied to the leaf (Bend)',
- min=0.0,
- max=1.0,
- default=0.0, update=update_tree)
+ default=1.0, update=update_leaves)
+ leafScaleT = FloatProperty(name='Leaf Scale Taper',
+ description='scale leaves toward the tip or base of the patent branch',
+ min = -1.0,
+ max = 1.0,
+ default=0.0, update=update_leaves)
+ leafScaleV = FloatProperty(name='Leaf Scale Variation',
+ description='randomize leaf scale',
+ min = 0.0,
+ max = 1.0,
+ default=0.0, update=update_leaves)
+ leafShape = EnumProperty(name='Leaf Shape',
+ description='The shape of the leaves',
+ items=(('hex', 'Hexagonal', '0'), ('rect', 'Rectangular', '1'), ('dFace', 'DupliFaces', '2'), ('dVert', 'DupliVerts', '3')),
+ default='hex', update=update_leaves)
+ leafDupliObj = EnumProperty(name='Leaf Object',
+ description='Object to use for leaf instancing if Leaf Shape is DupliFaces or DupliVerts',
+ items=objectList,
+ update=update_leaves)
+
+# bend = FloatProperty(name='Leaf Bend',
+# description='The proportion of bending applied to the leaf (Bend)',
+# min=0.0,
+# max=1.0,
+# default=0.0, update=update_leaves)
+
+ leafangle = FloatProperty(name='Leaf Angle',
+ description='Leaf vertical attraction',
+ default=0.0, update=update_leaves)
+ horzLeaves = BoolProperty(name='Horizontal leaves',
+ description='Leaves face upwards',
+ default=True, update=update_leaves)
leafDist = EnumProperty(name='Leaf Distribution',
description='The way leaves are distributed on branches',
- items=shapeList,
- default='4', update=update_tree)
+ items=shapeList4,
+ default='6', update=update_tree)
bevelRes = IntProperty(name='Bevel Resolution',
description='The bevel resolution of the curves',
min=0,
+ max=32,
default=0, update=update_tree)
resU = IntProperty(name='Curve Resolution',
description='The resolution along the curves',
@@ -361,21 +585,68 @@ class AddTree(bpy.types.Operator):
handleType = EnumProperty(name='Handle Type',
description='The type of handles used in the spline',
items=handleList,
- default='1', update=update_tree)
- frameRate = FloatProperty(name='Frame Rate',
- description=('The number of frames per second which can be used to '
- 'adjust the speed of animation'),
- min=0.001,
- default=1, update=update_tree)
- windSpeed = FloatProperty(name='Wind Speed',
- description='The wind speed to apply to the armature (WindSpeed)',
- default=2.0, update=update_tree)
- windGust = FloatProperty(name='Wind Gust',
- description='The greatest increase over Wind Speed (WindGust)',
- default=0.0, update=update_tree)
+ default='0', update=update_tree)
+
armAnim = BoolProperty(name='Armature Animation',
description='Whether animation is added to the armature',
default=False, update=update_tree)
+ previewArm = BoolProperty(name='Fast Preview',
+ description='Disable armature modifier, hide tree, and set bone display to wire, for fast playback',
+ ##Disable skin modifier and hide tree and armature, for fast playback
+ default=False, update=update_tree)
+ leafAnim = BoolProperty(name='Leaf Animation',
+ description='Whether animation is added to the leaves',
+ default=False, update=update_tree)
+ frameRate = FloatProperty(name='Animation Speed',
+ description=('Adjust speed of animation, relative to scene frame rate'),
+ min=0.001,
+ default=1, update=update_tree)
+ loopFrames = IntProperty(name='Loop Frames',
+ description='Number of frames to make the animation loop for, zero is disabled',
+ min=0,
+ default=0, update=update_tree)
+
+# windSpeed = FloatProperty(name='Wind Speed',
+# description='The wind speed to apply to the armature',
+# default=2.0, update=update_tree)
+# windGust = FloatProperty(name='Wind Gust',
+# description='The greatest increase over Wind Speed',
+# default=0.0, update=update_tree)
+
+ wind= FloatProperty(name='Overall Wind Strength',
+ description='The intensity of the wind to apply to the armature',
+ default=1.0, update=update_tree)
+
+ gust = FloatProperty(name='Wind Gust Strength',
+ description='The amount of directional movement, (from the positive Y direction)',
+ default=1.0, update=update_tree)
+
+ gustF = FloatProperty(name='Wind Gust Fequency',
+ description='The Fequency of directional movement',
+ default=0.075, update=update_tree)
+
+ af1 = FloatProperty(name='Amplitude',
+ description='Multiplier for noise amplitude',
+ default=1.0, update=update_tree)
+ af2 = FloatProperty(name='Frequency',
+ description='Multiplier for noise fequency',
+ default=1.0, update=update_tree)
+ af3 = FloatProperty(name='Randomness',
+ description='Random offset in noise',
+ default=4.0, update=update_tree)
+
+ makeMesh = BoolProperty(name='Make Mesh',
+ description='Convert curves to mesh, uses skin modifier, enables armature simplification',
+ default=False, update=update_tree)
+ armLevels = IntProperty(name='Armature Levels',
+ description='Number of branching levels to make bones for, 0 is all levels',
+ min=0,
+ default=2, update=update_tree)
+ boneStep = IntVectorProperty(name='Bone Length',
+ description='Number of stem segments per bone',
+ min=1,
+ default=[1, 1, 1, 1],
+ size=4, update=update_tree)
presetName = StringProperty(name='Preset Name',
description='The name of the preset to be saved',
@@ -384,22 +655,21 @@ class AddTree(bpy.types.Operator):
limitImport = BoolProperty(name='Limit Import',
description='Limited imported tree to 2 levels & no leaves for speed',
default=True, update=no_update_tree)
+ overwrite = BoolProperty(name='Overwrite',
+ description='When checked, overwrite existing preset files when saving',
+ default=False, update=no_update_tree)
- startCurv = FloatProperty(name='Trunk Starting Angle',
- description=('The angle between vertical and the starting direction '
- 'of the trunk'),
- min=0.0,
- max=360,
- default=0.0, update=update_tree)
+# startCurv = FloatProperty(name='Trunk Starting Angle',
+# description=('The angle between vertical and the starting direction '
+# 'of the trunk'),
+# min=0.0,
+# max=360,
+# default=0.0, update=update_tree)
@classmethod
def poll(cls, context):
return context.mode == 'OBJECT'
- def check(self, context):
- # TODO, should check exact vars which require redraw
- return True
-
def draw(self, context):
layout = self.layout
@@ -419,25 +689,27 @@ class AddTree(bpy.types.Operator):
row.prop(self, 'resU')
box.prop(self, 'handleType')
- sub = box.row()
- sub.active = self.levels >= 3
- sub.prop(self, 'shape')
+ box.prop(self, 'shape')
+
+ col = box.column()
+ col.prop(self, 'customShape')
+
+ row = box.row()
+ box.prop(self, 'shapeS')
+ box.prop(self, 'branchDist')
+ box.prop(self, 'nrings')
box.prop(self, 'seed')
- box.prop(self, 'ratio')
+ box.label("Tree Scale:")
row = box.row()
row.prop(self, 'scale')
row.prop(self, 'scaleV')
- row = box.row()
- row.prop(self, 'scale0')
- row.prop(self, 'scaleV0')
-
# Here we create a dict of all the properties.
# Unfortunately as_keyword doesn't work with vector properties,
# so we need something custom. This is it
data = []
- for a, b in (self.as_keywords(ignore=("chooseSet", "presetName", "limitImport", "do_update"))).items():
+ for a, b in (self.as_keywords(ignore=("chooseSet", "presetName", "limitImport", "do_update", "overwrite", "leafDupliObj"))).items():
# If the property is a vector property then add the slice to the list
try:
len(b)
@@ -451,94 +723,173 @@ class AddTree(bpy.types.Operator):
row = box.row()
row.prop(self, 'presetName')
# Send the data dict and the file name to the exporter
- row.operator('sapling.exportdata').data = repr([repr(data),
- self.presetName])
+ row.operator('sapling.exportdata').data = repr([repr(data), self.presetName, self.overwrite])
+ row = box.row()
+ row.label(" ")
+ row.prop(self, 'overwrite')
row = box.row()
row.menu('sapling.presetmenu', text='Load Preset')
row.prop(self, 'limitImport')
elif self.chooseSet == '1':
box = layout.box()
+ box.label("Branch Radius:")
+
+ row = box.row()
+ row.prop(self, 'bevel')
+ row.prop(self, 'bevelRes')
+
+ box.prop(self, 'ratio')
+ row = box.row()
+ row.prop(self, 'scale0')
+ row.prop(self, 'scaleV0')
+ box.prop(self, 'ratioPower')
+
+ box.prop(self, 'minRadius')
+ box.prop(self, 'closeTip')
+ box.prop(self, 'rootFlare')
+
+ box.prop(self, 'autoTaper')
+
+ split = box.split()
+ col = split.column()
+ col.prop(self, 'taper')
+ col = split.column()
+ col.prop(self, 'radiusTweak')
+
+ elif self.chooseSet == '2':
+ box = layout.box()
box.label("Branch Splitting:")
box.prop(self, 'levels')
box.prop(self, 'baseSplits')
- box.prop(self, 'baseSize')
+ row = box.row()
+ row.prop(self, 'baseSize')
+ row.prop(self, 'baseSize_s')
+ box.prop(self, 'splitHeight')
+ box.prop(self, 'splitBias')
+ box.prop(self, 'splitByLen')
split = box.split()
col = split.column()
col.prop(self, 'branches')
col.prop(self, 'splitAngle')
- col.prop(self, 'downAngle')
col.prop(self, 'rotate')
+ col.prop(self, 'attractOut')
col = split.column()
col.prop(self, 'segSplits')
col.prop(self, 'splitAngleV')
- col.prop(self, 'downAngleV')
col.prop(self, 'rotateV')
- box.prop(self, 'ratioPower')
+ col.label("Branching Mode:")
+ col.prop(self, 'rMode')
- elif self.chooseSet == '2':
+ box.column().prop(self, 'curveRes')
+
+ elif self.chooseSet == '3':
box = layout.box()
box.label("Branch Growth:")
- box.prop(self, 'startCurv')
- box.prop(self, 'attractUp')
+
+ box.prop(self, 'taperCrown')
split = box.split()
col = split.column()
col.prop(self, 'length')
+ col.prop(self, 'downAngle')
col.prop(self, 'curve')
col.prop(self, 'curveBack')
col = split.column()
col.prop(self, 'lengthV')
+ col.prop(self, 'downAngleV')
col.prop(self, 'curveV')
- col.prop(self, 'taper')
+ col.prop(self, 'attractUp')
- box.column().prop(self, 'curveRes')
+ box.prop(self, 'useOldDownAngle')
+ box.prop(self, 'useParentAngle')
- elif self.chooseSet == '3':
+ elif self.chooseSet == '4':
box = layout.box()
- box.label("Pruning:")
+ box.label("Prune:")
box.prop(self, 'prune')
box.prop(self, 'pruneRatio')
- box.prop(self, 'pruneWidth')
+ row = box.row()
+ row.prop(self, 'pruneWidth')
+ row.prop(self, 'pruneBase')
box.prop(self, 'pruneWidthPeak')
row = box.row()
row.prop(self, 'prunePowerHigh')
row.prop(self, 'prunePowerLow')
- elif self.chooseSet == '4':
+ elif self.chooseSet == '5':
box = layout.box()
box.label("Leaves:")
box.prop(self, 'showLeaves')
box.prop(self, 'leafShape')
+ box.prop(self, 'leafDupliObj')
box.prop(self, 'leaves')
box.prop(self, 'leafDist')
+ box.label("")
+ row = box.row()
+ row.prop(self, 'leafDownAngle')
+ row.prop(self, 'leafDownAngleV')
+
+ row = box.row()
+ row.prop(self, 'leafRotate')
+ row.prop(self, 'leafRotateV')
+ box.label("")
+
row = box.row()
row.prop(self, 'leafScale')
row.prop(self, 'leafScaleX')
- box.prop(self, 'bend')
+ row = box.row()
+ row.prop(self, 'leafScaleT')
+ row.prop(self, 'leafScaleV')
- elif self.chooseSet == '5':
- box = layout.box()
- box.label("Armature and Animation:")
+ box.prop(self, 'horzLeaves')
+ box.prop(self, 'leafangle')
+
+ #box.label(" ")
+ #box.prop(self, 'bend')
+ elif self.chooseSet == '6':
+ box = layout.box()
+ box.label("Armature:")
row = box.row()
row.prop(self, 'useArm')
- row.prop(self, 'armAnim')
+ box.prop(self, 'makeMesh')
+ box.label("Armature Simplification:")
+ box.prop(self, 'armLevels')
+ box.prop(self, 'boneStep')
+
+ elif self.chooseSet == '7':
+ box = layout.box()
+ box.label("Finalize All Other Settings First!")
+ box.prop(self, 'armAnim')
+ box.prop(self, 'leafAnim')
+ box.prop(self, 'previewArm')
+ box.prop(self, 'frameRate')
+ box.prop(self, 'loopFrames')
+
+ #row = box.row()
+ #row.prop(self, 'windSpeed')
+ #row.prop(self, 'windGust')
+ box.label('Wind Settings:')
+ box.prop(self, 'wind')
row = box.row()
- row.prop(self, 'windSpeed')
- row.prop(self, 'windGust')
+ row.prop(self, 'gust')
+ row.prop(self, 'gustF')
- box.prop(self, 'frameRate')
+ box.label('Leaf Wind Settings:')
+ box.prop(self, 'af1')
+ box.prop(self, 'af2')
+ box.prop(self, 'af3')
def execute(self, context):
# Ensure the use of the global variables
@@ -550,12 +901,13 @@ class AddTree(bpy.types.Operator):
for a, b in settings.items():
setattr(self, a, b)
if self.limitImport:
- setattr(self, 'levels', 2)
+ setattr(self, 'levels', min(settings['levels'], 2))
setattr(self, 'showLeaves', False)
useSet = False
if not self.do_update:
return {'PASS_THROUGH'}
addTree(self)
+ #cProfile.runctx("addTree(self)", globals(), locals())
print("Tree creation in %0.1fs" %(time.time()-start_time))
return {'FINISHED'}
@@ -565,17 +917,14 @@ class AddTree(bpy.types.Operator):
bpy.ops.sapling.importdata(filename = "quaking_aspen.py")
return self.execute(context)
-
def menu_func(self, context):
- self.layout.operator(AddTree.bl_idname, text="Add Tree", icon='PLUGIN')
-
+ self.layout.operator(AddTree.bl_idname, text="Sapling Tree Gen", icon='CURVE_DATA')
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_curve_add.append(menu_func)
-
def unregister():
bpy.utils.unregister_module(__name__)
diff --git a/add_curve_sapling/presets/Callistemon.py b/add_curve_sapling/presets/Callistemon.py
new file mode 100644
index 00000000..6a70ab49
--- /dev/null
+++ b/add_curve_sapling/presets/Callistemon.py
@@ -0,0 +1 @@
+{'handleType': '0', 'rotate': (99.5, 137.5, 137.5, 137.5), 'baseSize_s': 0.1600000560283661, 'af2': 1.0, 'pruneRatio': 0.75, 'radiusTweak': (1.0, 1.0, 1.0, 1.0), 'pruneWidthPeak': 0.5, 'boneStep': (1, 1, 1, 1), 'nrings': 0, 'leafScale': 0.4000000059604645, 'makeMesh': False, 'baseSize': 0.30000001192092896, 'lengthV': (0.0, 0.10000000149011612, 0.0, 0.0), 'shapeS': '10', 'pruneBase': 0.11999999731779099, 'af3': 4.0, 'loopFrames': 0, 'horzLeaves': True, 'curveRes': (8, 5, 3, 1), 'minRadius': 0.001500000013038516, 'leafDist': '6', 'rotateV': (15.0, 0.0, 0.0, 0.0), 'bevel': True, 'curveBack': (0.0, 0.0, 0.0, 0.0), 'leafScaleV': 0.15000000596046448, 'prunePowerHigh': 0.5, 'rootFlare': 1.0, 'prune': False, 'branches': (0, 55, 10, 1), 'taperCrown': 0.5, 'useArm': False, 'splitBias': 0.5499999523162842, 'segSplits': (0.10000000149011612, 0.5, 0.20000000298023224, 0.0), 'resU': 4, 'useParentAngle': True, 'ratio': 0.014999999664723873, 'taper': (1.0, 1.0, 1.0, 1.0), 'length': (0.800000011920929, 0.6000000238418579, 0.5, 0.10000000149011612), 'scale0': 1.0, 'scaleV': 2.0, 'leafRotate': 137.5, 'shape': '7', 'scaleV0': 0.10000000149011612, 'leaves': 150, 'scale': 5.0, 'leafShape': 'hex', 'prunePowerLow': 0.0010000000474974513, 'splitAngle': (18.0, 18.0, 22.0, 0.0), 'seed': 0, 'showLeaves': True, 'downAngle': (0.0, 26.209999084472656, 52.55999755859375, 30.0), 'leafDownAngle': 30.0, 'autoTaper': True, 'rMode': 'rotate', 'leafScaleX': 0.20000000298023224, 'leafScaleT': 0.10000000149011612, 'gust': 1.0, 'armAnim': False, 'wind': 1.0, 'leafRotateV': 15.0, 'baseSplits': 3, 'attractOut': (0.0, 0.800000011920929, 0.0, 0.0), 'armLevels': 2, 'leafAnim': False, 'ratioPower': 1.2000000476837158, 'splitHeight': 0.20000000298023224, 'splitByLen': True, 'af1': 1.0, 'branchDist': 1.5, 'closeTip': False, 'previewArm': False, 'attractUp': (3.5, -1.899843692779541, 0.0, 0.0), 'bevelRes': 1, 'pruneWidth': 0.3400000035762787, 'gustF': 0.07500000298023224, 'leafangle': -12.0, 'curveV': (20.0, 50.0, 75.0, 0.0), 'useOldDownAngle': True, 'leafDownAngleV': -10.0, 'frameRate': 1.0, 'splitAngleV': (5.0, 5.0, 5.0, 0.0), 'levels': 2, 'downAngleV': (0.0, 10.0, 10.0, 10.0), 'customShape': (0.5, 1.0, 0.30000001192092896, 0.5), 'curve': (0.0, -15.0, 0.0, 0.0)} \ No newline at end of file
diff --git a/add_curve_sapling/presets/Douglas Fir.py b/add_curve_sapling/presets/Douglas Fir.py
new file mode 100644
index 00000000..0b61d453
--- /dev/null
+++ b/add_curve_sapling/presets/Douglas Fir.py
@@ -0,0 +1 @@
+{'pruneRatio': 1.0, 'wind': 1.0, 'prune': False, 'splitByLen': True, 'attractUp': (0.0, 0.3499999940395355, 0.25, 0.15000000596046448), 'segSplits': (0.0, 0.3499999940395355, 0.6000000238418579, 0.0), 'rMode': 'rotate', 'rotate': (99.5, 137.5, -45.0, -60.0), 'curve': (0.0, 20.0, -10.0, 0.0), 'af2': 1.0, 'seed': 0, 'length': (1.0, 0.20000000298023224, 0.550000011920929, 0.44999998807907104), 'attractOut': (0.0, 0.0, 0.0, 0.0), 'leafScaleT': 0.0, 'scaleV': 15.0, 'splitHeight': 0.20000000298023224, 'minRadius': 0.0020000000949949026, 'leafRotate': 137.5, 'curveRes': (12, 4, 3, 2), 'curveV': (40.0, 30.0, 15.0, 10.0), 'nrings': 0, 'levels': 4, 'frameRate': 1.0, 'ratioPower': 1.2000000476837158, 'leafDownAngle': 45.0, 'armLevels': 2, 'branches': (0, 75, 16, 10), 'splitAngleV': (0.0, 0.0, 0.0, 0.0), 'shape': '8', 'rotateV': (15.0, 0.0, 15.0, 45.0), 'loopFrames': 0, 'curveBack': (0.0, 0.0, 0.0, 0.0), 'taperCrown': 0.0, 'prunePowerLow': 0.0010000000474974513, 'handleType': '0', 'af3': 4.0, 'useParentAngle': True, 'customShape': (0.5, 1.0, 0.3499999940395355, 0.10000000149011612), 'scale0': 1.0, 'rootFlare': 1.2999999523162842, 'leafScaleV': 0.0, 'leafScale': 0.17000000178813934, 'leafangle': 0.0, 'scaleV0': 0.10000000149011612, 'downAngleV': (0.0, 30.0, 10.0, 10.0), 'af1': 1.0, 'autoTaper': True, 'scale': 35.0, 'gustF': 0.07500000298023224, 'ratio': 0.014999999664723873, 'leafScaleX': 1.0, 'shapeS': '7', 'bevelRes': 1, 'bevel': True, 'leaves': 25, 'splitAngle': (0.0, 12.0, 18.0, 0.0), 'downAngle': (90.0, 130.0, 45.0, 45.0), 'previewArm': False, 'radiusTweak': (1.0, 1.0, 1.0, 1.0), 'showLeaves': False, 'leafAnim': False, 'baseSize': 0.33000001311302185, 'gust': 1.0, 'horzLeaves': True, 'baseSize_s': 0.5, 'leafDownAngleV': 10.0, 'makeMesh': False, 'leafDist': '6', 'armAnim': False, 'baseSplits': 0, 'lengthV': (0.0, 0.10000000149011612, 0.25, 0.25), 'branchDist': 1.850000023841858, 'useArm': False, 'useOldDownAngle': False, 'taper': (1.0, 1.0, 1.0, 1.0), 'pruneBase': 0.30000001192092896, 'splitBias': 0.0, 'boneStep': (1, 1, 1, 1), 'leafShape': 'hex', 'resU': 4, 'prunePowerHigh': 0.5, 'closeTip': True, 'pruneWidth': 0.4000000059604645, 'leafRotateV': 0.0, 'pruneWidthPeak': 0.6000000238418579} \ No newline at end of file
diff --git a/add_curve_sapling/presets/Japanese Maple.py b/add_curve_sapling/presets/Japanese Maple.py
new file mode 100644
index 00000000..4ce72059
--- /dev/null
+++ b/add_curve_sapling/presets/Japanese Maple.py
@@ -0,0 +1 @@
+{'leafScaleT': -0.5, 'shapeS': '10', 'scaleV': 2.0, 'resU': 4, 'boneStep': (1, 1, 1, 1), 'af3': 4.0, 'baseSize': 0.4000000059604645, 'prunePowerLow': 0.0010000000474974513, 'leafRotateV': 0.0, 'rootFlare': 1.0, 'customShape': (0.699999988079071, 1.0, 0.20000000298023224, 0.800000011920929), 'attractOut': (0.0, 0.75, 0.25, 0.0), 'useArm': False, 'branches': (0, 50, 10, 16), 'leafDownAngle': 45.0, 'length': (1.0, 0.30000001192092896, 0.5, 0.20000000298023224), 'segSplits': (0.25, 0.4000000059604645, 0.5, 0.0), 'makeMesh': False, 'curveV': (400.0, 150.0, 100.0, 0.0), 'curveBack': (0.0, 0.0, 0.0, 0.0), 'af1': 1.0, 'closeTip': False, 'frameRate': 1.0, 'leafangle': -10.0, 'af2': 1.0, 'rMode': 'rotate', 'leafScaleV': 0.0, 'rotateV': (15.0, 0.0, 0.0, 0.0), 'useParentAngle': False, 'taperCrown': 0.0, 'minRadius': 0.001500000013038516, 'splitAngleV': (5.0, 5.0, 0.0, 0.0), 'scaleV0': 0.10000000149011612, 'bevel': True, 'leafDownAngleV': 10.0, 'previewArm': False, 'showLeaves': True, 'ratioPower': 1.25, 'handleType': '0', 'branchDist': 1.0, 'leafScaleX': 0.20000000298023224, 'prune': False, 'splitHeight': 0.30000001192092896, 'baseSplits': 2, 'baseSize_s': 0.25, 'downAngle': (90.0, 90.0, 30.0, 30.0), 'bevelRes': 1, 'leafAnim': False, 'loopFrames': 0, 'lengthV': (0.0, 0.0, 0.0, 0.0), 'gust': 1.0, 'downAngleV': (0.0, 90.0, 15.0, 10.0), 'leafRotate': 137.5, 'wind': 1.0, 'leaves': -5, 'curve': (0.0, -20.0, -20.0, 0.0), 'radiusTweak': (1.0, 1.0, 1.0, 1.0), 'pruneRatio': 1.0, 'pruneBase': 0.30000001192092896, 'armAnim': False, 'splitBias': 0.0, 'rotate': (99.5, 137.5, 137.5, 137.5), 'armLevels': 2, 'scale': 6.0, 'prunePowerHigh': 0.5, 'nrings': 0, 'splitByLen': True, 'leafShape': 'hex', 'splitAngle': (15.0, 20.0, 25.0, 0.0), 'ratio': 0.019999999552965164, 'scale0': 1.0, 'autoTaper': True, 'pruneWidth': 0.4000000059604645, 'leafScale': 0.17000000178813934, 'seed': 0, 'curveRes': (16, 5, 3, 1), 'horzLeaves': True, 'useOldDownAngle': False, 'levels': 4, 'pruneWidthPeak': 0.6000000238418579, 'attractUp': (0.0, -0.3499999940395355, -0.20000000298023224, 0.0), 'taper': (1.0, 1.0, 1.0, 1.0), 'leafDist': '6', 'gustF': 0.07500000298023224, 'shape': '8'} \ No newline at end of file
diff --git a/add_curve_sapling/presets/Small Maple.py b/add_curve_sapling/presets/Small Maple.py
new file mode 100644
index 00000000..a5e91185
--- /dev/null
+++ b/add_curve_sapling/presets/Small Maple.py
@@ -0,0 +1 @@
+{'leafDownAngle': 45.0, 'handleType': '0', 'radiusTweak': (1.0, 1.0, 1.0, 1.0), 'useArm': False, 'rootFlare': 1.0, 'segSplits': (0.44999998807907104, 0.5, 0.800000011920929, 0.0), 'leafRotateV': 0.0, 'leaves': 32, 'baseSplits': 1, 'rotate': (99.5, 137.5, 137.5, 137.5), 'scale': 8.0, 'makeMesh': False, 'leafDownAngleV': 10.0, 'curve': (0.0, 30.0, 0.0, 0.0), 'splitBias': 0.0, 'leafScale': 0.18000000715255737, 'levels': 3, 'leafScaleT': 0.3499999940395355, 'seed': 0, 'autoTaper': True, 'pruneWidthPeak': 0.6000000238418579, 'branchDist': 1.25, 'prune': False, 'splitAngle': (8.0, 18.0, 18.0, 0.0), 'shapeS': '7', 'useParentAngle': True, 'af1': 1.0, 'armAnim': False, 'resU': 4, 'ratioPower': 1.2000000476837158, 'scaleV': 2.0, 'splitHeight': 0.20000000298023224, 'leafRotate': 137.5, 'customShape': (0.5, 1.0, 0.30000001192092896, 0.5), 'shape': '7', 'ratio': 0.019999999552965164, 'rMode': 'rotate', 'downAngle': (90.0, 48.0, 45.0, 45.0), 'bevelRes': 2, 'showLeaves': True, 'wind': 1.0, 'loopFrames': 0, 'splitByLen': True, 'lengthV': (0.0, 0.029999999329447746, 0.15000000596046448, 0.0), 'leafShape': 'hex', 'downAngleV': (0.0, 48.0, 10.0, 10.0), 'leafangle': -35.0, 'bevel': True, 'gustF': 0.07500000298023224, 'taper': (1.0, 1.0, 1.0, 1.0), 'previewArm': False, 'frameRate': 1.0, 'curveBack': (0.0, -30.0, -20.0, 0.0), 'taperCrown': 0.20000000298023224, 'rotateV': (15.0, 0.0, 0.0, 0.0), 'scaleV0': 0.20000000298023224, 'gust': 1.0, 'horzLeaves': True, 'attractUp': (-0.5, -0.699999988079071, 0.0, 0.0), 'splitAngleV': (2.0, 5.0, 5.0, 0.0), 'pruneRatio': 1.0, 'curveV': (10.0, 35.0, 35.0, 0.0), 'prunePowerHigh': 0.5, 'closeTip': False, 'leafScaleV': 0.20000000298023224, 'leafAnim': False, 'minRadius': 0.0020000000949949026, 'af3': 4.0, 'attractOut': (0.0, 0.4000000059604645, 0.5, 0.0), 'branches': (0, 110, 18, 10), 'leafScaleX': 0.6499999761581421, 'length': (1.0, 0.46000000834465027, 0.44999998807907104, 0.44999998807907104), 'useOldDownAngle': False, 'armLevels': 2, 'boneStep': (1, 1, 1, 1), 'pruneBase': 0.30000001192092896, 'curveRes': (8, 5, 3, 1), 'prunePowerLow': 0.0010000000474974513, 'af2': 1.0, 'pruneWidth': 0.4000000059604645, 'nrings': 0, 'baseSize_s': 0.25, 'baseSize': 0.2800000011920929, 'leafDist': '6', 'scale0': 1.0} \ No newline at end of file
diff --git a/add_curve_sapling/presets/Small Pine.py b/add_curve_sapling/presets/Small Pine.py
new file mode 100644
index 00000000..860d885c
--- /dev/null
+++ b/add_curve_sapling/presets/Small Pine.py
@@ -0,0 +1 @@
+{'downAngleV': (0.0, 42.0, 10.0, 10.0), 'useOldDownAngle': False, 'splitBias': 0.0, 'branchDist': 1.600000023841858, 'downAngle': (90.0, 110.0, 45.0, 45.0), 'leafScale': 0.20000000298023224, 'baseSize': 0.06799984723329544, 'shapeS': '4', 'leafScaleX': 0.019999999552965164, 'nrings': 7, 'gustF': 0.07500000298023224, 'showLeaves': True, 'taperCrown': 0.0, 'curveBack': (0.0, 0.0, 0.0, 0.0), 'leafShape': 'rect', 'makeMesh': False, 'scale0': 1.0, 'length': (1.0, 0.3199999928474426, 0.75, 0.44999998807907104), 'ratioPower': 1.0, 'taper': (1.0, 1.0, 1.0, 1.0), 'baseSize_s': 0.25, 'splitHeight': 0.20000000298023224, 'handleType': '0', 'pruneBase': 0.30000001192092896, 'attractUp': (2.0, 0.0, 0.5, 0.5), 'boneStep': (1, 1, 1, 1), 'seed': 0, 'leafDownAngle': 65.0, 'attractOut': (0.0, 0.0, 0.0, 0.0), 'leafAnim': False, 'gust': 1.0, 'curveV': (100.0, 100.0, 100.0, 0.0), 'splitAngle': (0.0, 22.0, 25.0, 0.0), 'prunePowerLow': 0.0010000000474974513, 'leafangle': -10.0, 'frameRate': 1.0, 'pruneRatio': 1.0, 'rMode': 'rotate', 'shape': '8', 'segSplits': (0.0, 0.30000001192092896, 0.4000000059604645, 0.0), 'branches': (0, 36, 7, 10), 'prunePowerHigh': 0.5, 'af1': 1.0, 'closeTip': False, 'splitAngleV': (0.0, 5.0, 0.0, 0.0), 'ratio': 0.019999999552965164, 'minRadius': 0.001500000013038516, 'levels': 3, 'leafRotate': 137.5, 'armLevels': 2, 'horzLeaves': False, 'pruneWidth': 0.4000000059604645, 'baseSplits': 0, 'scale': 4.0, 'leafScaleV': 0.10000000149011612, 'splitByLen': True, 'useParentAngle': True, 'previewArm': False, 'wind': 1.0, 'leafDist': '3', 'leafScaleT': 0.25, 'bevel': True, 'resU': 4, 'leafDownAngleV': 55.0, 'pruneWidthPeak': 0.6000000238418579, 'af3': 4.0, 'scaleV': 1.0, 'rootFlare': 1.0, 'loopFrames': 0, 'curve': (0.0, -40.0, -30.0, 0.0), 'leaves': 500, 'scaleV0': 0.10000000149011612, 'rotate': (99.5, 137.5, -90.0, 137.5), 'curveRes': (8, 5, 3, 1), 'useArm': False, 'af2': 1.0, 'bevelRes': 2, 'autoTaper': True, 'lengthV': (0.0, 0.15000000596046448, 0.25, 0.0), 'armAnim': False, 'prune': False, 'radiusTweak': (1.0, 1.0, 1.0, 1.0), 'leafRotateV': 30.0, 'customShape': (0.8999999761581421, 1.0, 0.20000000298023224, 0.20000000298023224), 'rotateV': (15.0, 0.0, 0.0, 0.0)} \ No newline at end of file
diff --git a/add_curve_sapling/presets/Weeping Willow.py b/add_curve_sapling/presets/Weeping Willow.py
new file mode 100644
index 00000000..06f27e15
--- /dev/null
+++ b/add_curve_sapling/presets/Weeping Willow.py
@@ -0,0 +1 @@
+{'showLeaves': False, 'leafScaleX': 0.20000000298023224, 'horzLeaves': False, 'useArm': False, 'rMode': 'rotate', 'branchDist': 1.5, 'scale': 15.0, 'armLevels': 2, 'gustF': 0.07500000298023224, 'closeTip': False, 'shape': '4', 'minRadius': 0.001500000013038516, 'leafScaleT': 0.0, 'leafDownAngle': 30.0, 'splitByLen': True, 'prunePowerLow': 0.0010000000474974513, 'splitBias': 0.0, 'autoTaper': True, 'leafRotateV': 30.0, 'scale0': 1.0, 'length': (0.75, 0.5, 1.5, 0.10000000149011612), 'lengthV': (0.0, 0.10000000149011612, 0.0, 0.0), 'bevel': True, 'baseSize': 0.20000000298023224, 'handleType': '1', 'ratioPower': 1.75, 'leafScaleV': 0.0, 'ratio': 0.02500000037252903, 'scaleV': 5.0, 'gust': 1.0, 'baseSplits': 2, 'loopFrames': 0, 'curve': (0.0, 20.0, -40.0, 0.0), 'pruneWidth': 0.5, 'shapeS': '4', 'splitAngleV': (0.0, 10.0, 20.0, 0.0), 'branches': (0, 35, 15, 1), 'frameRate': 1.0, 'splitAngle': (12.0, 30.0, 16.0, 0.0), 'seed': 2789, 'nrings': 0, 'previewArm': False, 'pruneWidthPeak': 0.6000000238418579, 'af1': 1.0, 'splitHeight': 0.20000000298023224, 'rotateV': (15.0, 15.0, 45.0, 0.0), 'attractUp': (0.0, 0.0, -2.75, -3.0), 'curveV': (150.0, 120.0, 0.0, 0.0), 'makeMesh': False, 'leaves': 150, 'curveBack': (0.0, 20.0, 0.0, 0.0), 'taper': (1.0, 1.0, 1.0, 1.0), 'resU': 4, 'useOldDownAngle': False, 'pruneRatio': 0.800000011920929, 'levels': 3, 'taperCrown': 0.0, 'attractOut': (0.0, 0.0, 0.0, 0.0), 'leafRotate': 137.5, 'prunePowerHigh': 0.20000000298023224, 'prune': False, 'leafangle': 0.0, 'bevelRes': 1, 'curveRes': (8, 16, 8, 1), 'rotate': (99.5, 137.5, -60.0, 140.0), 'pruneBase': 0.07000000029802322, 'segSplits': (0.10000000149011612, 0.20000000298023224, 0.20000000298023224, 0.0), 'scaleV0': 0.0, 'boneStep': (1, 1, 1, 1), 'useParentAngle': True, 'armAnim': False, 'wind': 1.0, 'leafDist': '10', 'leafScale': 0.25, 'radiusTweak': (1.0, 1.0, 1.0, 1.0), 'leafDownAngleV': 10.0, 'leafAnim': False, 'downAngle': (0.0, 20.0, 30.0, 20.0), 'af2': 1.0, 'af3': 4.0, 'downAngleV': (0.0, 20.0, 10.0, 10.0), 'customShape': (0.5, 1.0, 0.30000001192092896, 0.5), 'leafShape': 'hex', 'rootFlare': 1.0, 'baseSize_s': 0.25} \ No newline at end of file
diff --git a/add_curve_sapling/presets/White Birch.py b/add_curve_sapling/presets/White Birch.py
new file mode 100644
index 00000000..9016ee18
--- /dev/null
+++ b/add_curve_sapling/presets/White Birch.py
@@ -0,0 +1 @@
+{'seed': 0, 'showLeaves': True, 'armLevels': 0, 'leafDist': '6', 'baseSize': 0.3499999940395355, 'loopFrames': 0, 'af3': 4.0, 'previewArm': False, 'leafangle': -45.0, 'useParentAngle': True, 'handleType': '0', 'branches': (0, 60, 30, 10), 'autoTaper': True, 'splitAngle': (12.0, 18.0, 16.0, 0.0), 'baseSize_s': 0.800000011920929, 'closeTip': False, 'af2': 1.0, 'prune': False, 'scale0': 1.0, 'rMode': 'rotate', 'useOldDownAngle': False, 'scaleV0': 0.10000000149011612, 'splitBias': 0.0, 'resU': 2, 'curveBack': (0.0, -5.0, 0.0, 0.0), 'scale': 12.0, 'shape': '8', 'leafDownAngle': 45.0, 'af1': 1.0, 'ratio': 0.019999999552965164, 'horzLeaves': True, 'leafRotate': 137.5, 'minRadius': 0.0020000000949949026, 'bevelRes': 2, 'splitByLen': True, 'rootFlare': 1.149999976158142, 'makeMesh': False, 'downAngleV': (0.0, 25.0, 30.0, 10.0), 'levels': 3, 'scaleV': 2.0, 'armAnim': False, 'lengthV': (0.05000000074505806, 0.20000000298023224, 0.3499999940395355, 0.0), 'pruneWidth': 0.3100000023841858, 'gustF': 0.07500000298023224, 'taper': (1.0, 1.0, 1.0, 1.0), 'splitAngleV': (2.0, 2.0, 0.0, 0.0), 'prunePowerLow': 0.0010000000474974513, 'leafScaleT': 0.20000000298023224, 'leafScaleX': 0.5, 'leafRotateV': 0.0, 'ratioPower': 1.399999976158142, 'segSplits': (0.3499999940395355, 0.3499999940395355, 0.3499999940395355, 0.0), 'downAngle': (90.0, 60.0, 50.0, 45.0), 'rotateV': (0.0, 0.0, 0.0, 0.0), 'gust': 1.0, 'attractUp': (0.0, -1.0, -0.6499999761581421, 0.0), 'leafScaleV': 0.25, 'frameRate': 1.0, 'curveV': (100.0, 80.0, 80.0, 0.0), 'boneStep': (1, 1, 1, 1), 'customShape': (0.699999988079071, 1.0, 0.30000001192092896, 0.5900000333786011), 'pruneBase': 0.30000001192092896, 'leafAnim': False, 'curveRes': (10, 8, 3, 1), 'nrings': 0, 'bevel': True, 'taperCrown': 0.0, 'baseSplits': 2, 'leafShape': 'hex', 'splitHeight': 0.550000011920929, 'wind': 1.0, 'curve': (0.0, -30.0, -25.0, 0.0), 'rotate': (137.5, 137.5, 137.5, 137.5), 'length': (1.0, 0.33000001311302185, 0.375, 0.44999998807907104), 'leafScale': 0.20000000298023224, 'attractOut': (0.0, 0.20000000298023224, 0.25, 0.0), 'prunePowerHigh': 0.10000000149011612, 'branchDist': 1.5, 'useArm': False, 'pruneRatio': 1.0, 'shapeS': '7', 'leafDownAngleV': 10.0, 'pruneWidthPeak': 0.5, 'radiusTweak': (1.0, 1.0, 1.0, 1.0), 'leaves': 16} \ No newline at end of file
diff --git a/add_curve_sapling/presets/black_tupelo.py b/add_curve_sapling/presets/black_tupelo.py
deleted file mode 100644
index 5d6151c3..00000000
--- a/add_curve_sapling/presets/black_tupelo.py
+++ /dev/null
@@ -1 +0,0 @@
-{'pruneWidthPeak': 0.6000000238418579, 'downAngleV': (0.0, -40.0, 10.0, 10.0), 'frameRate': 1.0, 'lengthV': (0.0, 0.05000000074505806, 0.10000000149011612, 0.0), 'shape': '4', 'seed': 0, 'bend': 0.0, 'armAnim': False, 'useArm': False, 'splitAngle': (0.0, 0.0, 0.0, 0.0), 'baseSize': 0.20000000298023224, 'baseSplits': 0, 'scaleV': 5.0, 'scale': 23.0, 'ratio': 0.014999999664723873, 'curveV': (40.0, 90.0, 150.0, 0.0), 'prunePowerHigh': 0.5, 'splitAngleV': (0.0, 0.0, 0.0, 0.0), 'resU': 4, 'segSplits': (0.0, 0.0, 0.0, 0.0), 'ratioPower': 1.2999999523162842, 'handleType': '1', 'length': (1.0, 0.30000001192092896, 0.6000000238418579, 0.4000000059604645), 'rotateV': (0.0, 0.0, 0.0, 0.0), 'attractUp': 0.5, 'scale0': 1.0, 'bevel': False, 'leafDist': '4', 'chooseSet': '0', 'levels': 4, 'downAngle': (90.0, 60.0, 30.0, 45.0), 'showLeaves': False, 'prunePowerLow': 0.0010000000474974513, 'scaleV0': 0.0, 'leafScaleX': 0.5, 'curveRes': (10, 10, 10, 1), 'rotate': (140.0, 140.0, 140.0, 140.0), 'branches': (0, 50, 25, 12), 'prune': False, 'bevelRes': 0, 'taper': (1.0, 1.0, 1.0, 1.0), 'pruneRatio': 1.0, 'leaves': 6, 'curve': (0.0, 0.0, -10.0, 0.0), 'leafScale': 0.30000001192092896, 'windSpeed': 2.0, 'pruneWidth': 0.4000000059604645, 'windGust': 0.0, 'startCurv': 0.0, 'curveBack': (0.0, 0.0, 0.0, 0.0)}
diff --git a/add_curve_sapling/presets/ca_black_oak.py b/add_curve_sapling/presets/ca_black_oak.py
deleted file mode 100644
index 50bfca3c..00000000
--- a/add_curve_sapling/presets/ca_black_oak.py
+++ /dev/null
@@ -1 +0,0 @@
-{'pruneWidthPeak': 0.6000000238418579, 'downAngleV': (0.0, -30.0, 10.0, 10.0), 'frameRate': 1.0, 'lengthV': (0.0, 0.10000000149011612, 0.05000000074505806, 0.0), 'shape': '2', 'seed': 0, 'bend': 0.0, 'armAnim': False, 'useArm': False, 'splitAngle': (10.0, 10.0, 10.0, 0.0), 'baseSize': 0.05000000074505806, 'baseSplits': 2, 'scaleV': 10.0, 'scale': 10.0, 'ratio': 0.017999999225139618, 'curveV': (90.0, 150.0, -30.0, 0.0), 'prunePowerHigh': 0.5, 'splitAngleV': (0.0, 10.0, 10.0, 0.0), 'resU': 4, 'segSplits': (0.4000000059604645, 0.20000000298023224, 0.10000000149011612, 0.0), 'ratioPower': 1.2999999523162842, 'handleType': '1', 'length': (1.0, 0.800000011920929, 0.20000000298023224, 0.4000000059604645), 'rotateV': (0.0, 0.0, 0.0, 0.0), 'attractUp': 0.800000011920929, 'scale0': 1.0, 'bevel': False, 'leafDist': '4', 'chooseSet': '0', 'levels': 3, 'downAngle': (0.0, 30.0, 45.0, 45.0), 'showLeaves': False, 'prunePowerLow': 0.0010000000474974513, 'scaleV0': 0.0, 'leafScaleX': 0.6600000262260437, 'curveRes': (8, 10, 3, 1), 'rotate': (0.0, 80.0, 140.0, 140.0), 'branches': (0, 40, 120, 0), 'prune': False, 'bevelRes': 0, 'taper': (1.0, 1.0, 1.0, 1.0), 'pruneRatio': 1.0, 'leaves': 25, 'curve': (0.0, 40.0, 0.0, 0.0), 'leafScale': 0.11999999731779099, 'windSpeed': 2.0, 'pruneWidth': 0.4000000059604645, 'windGust': 0.0, 'startCurv': 0.0, 'curveBack': (0.0, -70.0, 0.0, 0.0)}
diff --git a/add_curve_sapling/presets/quaking_aspen.py b/add_curve_sapling/presets/quaking_aspen.py
index 955b5db7..7c1c5f65 100644
--- a/add_curve_sapling/presets/quaking_aspen.py
+++ b/add_curve_sapling/presets/quaking_aspen.py
@@ -1 +1 @@
-{'pruneWidthPeak': 0.6000000238418579, 'downAngleV': (0.0, -50.0, 10.0, 10.0), 'frameRate': 1.0, 'lengthV': (0.0, 0.0, 0.0, 0.0), 'shape': '7', 'seed': 0, 'bend': 0.0, 'armAnim': False, 'useArm': False, 'splitAngle': (0.0, 0.0, 0.0, 0.0), 'baseSize': 0.4000000059604645, 'baseSplits': 0, 'scaleV': 3.0, 'scale': 13.0, 'ratio': 0.014999999664723873, 'curveV': (20.0, 50.0, 75.0, 0.0), 'prunePowerHigh': 0.5, 'splitAngleV': (0.0, 0.0, 0.0, 0.0), 'resU': 4, 'segSplits': (0.0, 0.0, 0.0, 0.0), 'ratioPower': 1.2000000476837158, 'handleType': '1', 'length': (1.0, 0.30000001192092896, 0.6000000238418579, 0.44999998807907104), 'rotateV': (0.0, 0.0, 0.0, 0.0), 'attractUp': 0.5, 'scale0': 1.0, 'bevel': False, 'leafDist': '4', 'chooseSet': '0', 'levels': 3, 'downAngle': (90.0, 60.0, 45.0, 45.0), 'showLeaves': False, 'prunePowerLow': 0.0010000000474974513, 'scaleV0': 0.20000000298023224, 'leafScaleX': 1.0, 'curveRes': (3, 5, 3, 1), 'rotate': (140.0, 140.0, 140.0, 77.0), 'branches': (0, 50, 30, 10), 'prune': False, 'bevelRes': 0, 'taper': (1.0, 1.0, 1.0, 1.0), 'pruneRatio': 1.0, 'leaves': 25, 'curve': (0.0, -40.0, -40.0, 0.0), 'leafScale': 0.17000000178813934, 'windSpeed': 2.0, 'pruneWidth': 0.4000000059604645, 'windGust': 0.0, 'startCurv': 0.0, 'curveBack': (0.0, 0.0, 0.0, 0.0)}
+{'leafScale': 0.17000000178813934, 'autoTaper': True, 'customShape': (0.5, 1.0, 0.30000001192092896, 0.5), 'leafShape': 'hex', 'curve': (0.0, -40.0, -40.0, 0.0), 'ratio': 0.014999999664723873, 'splitBias': 0.0, 'pruneWidth': 0.4000000059604645, 'downAngleV': (0.0, 80.0, 10.0, 10.0), 'rotate': (99.5, 137.5, 137.5, 137.5), 'pruneRatio': 1.0, 'leafDownAngle': 45.0, 'makeMesh': False, 'radiusTweak': (1.0, 1.0, 1.0, 1.0), 'rMode': 'rotate', 'splitAngleV': (0.0, 0.0, 0.0, 0.0), 'branchDist': 1.0, 'bevel': False, 'minRadius': 0.001500000013038516, 'prune': False, 'leafRotateV': 0.0, 'splitAngle': (0.0, 0.0, 0.0, 0.0), 'armAnim': False, 'boneStep': (1, 1, 1, 1), 'pruneBase': 0.30000001192092896, 'taperCrown': 0.0, 'baseSplits': 0, 'baseSize_s': 0.25, 'handleType': '0', 'baseSize': 0.4000000059604645, 'af1': 1.0, 'levels': 2, 'leafScaleV': 0.0, 'resU': 4, 'seed': 0, 'downAngle': (90.0, 110.0, 45.0, 45.0), 'leafangle': 0.0, 'scaleV0': 0.10000000149011612, 'prunePowerHigh': 0.5, 'splitByLen': True, 'wind': 1.0, 'shape': '7', 'prunePowerLow': 0.0010000000474974513, 'scale': 13.0, 'leafAnim': False, 'curveBack': (0.0, 0.0, 0.0, 0.0), 'leafScaleX': 1.0, 'horzLeaves': True, 'splitHeight': 0.20000000298023224, 'leafScaleT': 0.0, 'scaleV': 3.0, 'leafDist': '6', 'nrings': 0, 'curveRes': (8, 5, 3, 1), 'shapeS': '4', 'bevelRes': 0, 'useOldDownAngle': False, 'useParentAngle': True, 'armLevels': 2, 'scale0': 1.0, 'taper': (1.0, 1.0, 1.0, 1.0), 'pruneWidthPeak': 0.6000000238418579, 'previewArm': False, 'leaves': 25, 'ratioPower': 1.100000023841858, 'gustF': 0.07500000298023224, 'curveV': (20.0, 50.0, 75.0, 0.0), 'showLeaves': False, 'frameRate': 1.0, 'length': (1.0, 0.30000001192092896, 0.6000000238418579, 0.44999998807907104), 'branches': (0, 50, 30, 10), 'useArm': False, 'loopFrames': 0, 'gust': 1.0, 'af3': 4.0, 'closeTip': False, 'leafRotate': 137.5, 'attractUp': (0.0, 0.0, 0.5, 0.5), 'leafDownAngleV': 10.0, 'rootFlare': 1.0, 'af2': 1.0, 'lengthV': (0.0, 0.0, 0.0, 0.0), 'rotateV': (15.0, 0.0, 0.0, 0.0), 'attractOut': (0.0, 0.0, 0.0, 0.0), 'segSplits': (0.0, 0.0, 0.0, 0.0)} \ No newline at end of file
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)