1 # SPDX-License-Identifier: GPL-2.0-or-later
4 "name": "Sapling Tree Gen",
5 "author": "Andrew Hale (TrumanBlending), Aaron Buchler, CansecoGPC",
8 "location": "View3D > Add > Curve",
9 "description": ("Adds a parametric tree. The method is presented by "
10 "Jason Weber & Joseph Penn in their paper 'Creation and Rendering of "
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/sapling.html",
13 "category": "Add Curve",
18 importlib
.reload(utils
)
20 from add_curve_sapling
import utils
29 from bpy
.types
import (
33 from bpy
.props
import (
45 shapeList
= [('0', 'Conical (0)', 'Shape = 0'),
46 ('1', 'Spherical (1)', 'Shape = 1'),
47 ('2', 'Hemispherical (2)', 'Shape = 2'),
48 ('3', 'Cylindrical (3)', 'Shape = 3'),
49 ('4', 'Tapered Cylindrical (4)', 'Shape = 4'),
50 ('5', 'Flame (5)', 'Shape = 5'),
51 ('6', 'Inverse Conical (6)', 'Shape = 6'),
52 ('7', 'Tend Flame (7)', 'Shape = 7')]
54 shapeList3
= [('0', 'Conical', ''),
55 ('6', 'Inverse Conical', ''),
56 ('1', 'Spherical', ''),
57 ('2', 'Hemispherical', ''),
58 ('3', 'Cylindrical', ''),
59 ('4', 'Tapered Cylindrical', ''),
60 ('10', 'Inverse Tapered Cylindrical', ''),
62 ('7', 'Tend Flame', ''),
63 ('8', 'Custom Shape', '')]
65 shapeList4
= [('0', 'Conical', ''),
66 ('6', 'Inverse Conical', ''),
67 ('1', 'Spherical', ''),
68 ('2', 'Hemispherical', ''),
69 ('3', 'Cylindrical', ''),
70 ('4', 'Tapered Cylindrical', ''),
71 ('10', 'Inverse Tapered Cylindrical', ''),
73 ('7', 'Tend Flame', '')]
75 handleList
= [('0', 'Auto', 'Auto'),
76 ('1', 'Vector', 'Vector')]
78 settings
= [('0', 'Geometry', 'Geometry'),
79 ('1', 'Branch Radius', 'Branch Radius'),
80 ('2', 'Branch Splitting', 'Branch Splitting'),
81 ('3', 'Branch Growth', 'Branch Growth'),
82 ('4', 'Pruning', 'Pruning'),
83 ('5', 'Leaves', 'Leaves'),
84 ('6', 'Armature', 'Armature'),
85 ('7', 'Animation', 'Animation')]
87 branchmodes
= [("original", "Original", "rotate around each branch"),
88 ("rotate", "Rotate", "evenly distribute branches to point outward from center of tree"),
89 ("random", "Random", "choose random point")]
93 """Support user defined scripts directory
94 Find the first occurrence of add_curve_sapling/presets in possible script paths
95 and return it as preset path"""
97 script_file
= os
.path
.realpath(__file__
)
98 directory
= os
.path
.dirname(script_file
)
99 directory
= os
.path
.join(directory
, "presets")
103 def getPresetpaths():
104 """Return paths for both local and user preset folders"""
105 userDir
= os
.path
.join(bpy
.utils
.script_path_user(), 'presets', 'operator', 'add_curve_sapling')
107 if os
.path
.isdir(userDir
):
112 script_file
= os
.path
.realpath(__file__
)
113 directory
= os
.path
.dirname(script_file
)
114 localDir
= os
.path
.join(directory
, "presets")
116 return (localDir
, userDir
)
119 class ExportData(Operator
):
120 """This operator handles writing presets to file"""
121 bl_idname
= 'sapling.exportdata'
122 bl_label
= 'Export Preset'
124 data
: StringProperty()
126 def execute(self
, context
):
127 # Unpack some data from the input
128 data
, filename
, overwrite
= eval(self
.data
)
131 # Check whether the file exists by trying to open it.
132 f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'r')
134 # If it exists then report an error
135 self.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
139 # If it doesn't exist, create the file with the required data
140 f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'w')
147 fpath1
= os
.path
.join(getPresetpaths()[0], filename
+ '.py')
148 fpath2
= os
.path
.join(getPresetpaths()[1], filename
+ '.py')
150 if os
.path
.exists(fpath1
):
151 # If it exists in built-in presets then report an error
152 self
.report({'ERROR_INVALID_INPUT'}, 'Can\'t have same name as built-in preset')
154 elif (not os
.path
.exists(fpath2
)) or (os
.path
.exists(fpath2
) and overwrite
):
155 # if (it does not exist) or (exists and overwrite) then write file
157 # If it doesn't exist, create the file with the required data
158 f
= open(os
.path
.join(getPresetpaths()[1], filename
+ '.py'), 'w')
165 # If it exists then report an error
166 self
.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
170 class ImportData(Operator
):
171 """This operator handles importing existing presets"""
172 bl_idname
= "sapling.importdata"
173 bl_label
= "Import Preset"
175 filename
: StringProperty()
177 def execute(self
, context
):
178 # Make sure the operator knows about the global variables
179 global settings
, useSet
180 # Read the preset data into the global settings
182 f
= open(os
.path
.join(getPresetpaths()[0], self
.filename
), 'r')
183 except (FileNotFoundError
, IOError):
184 f
= open(os
.path
.join(getPresetpaths()[1], self
.filename
), 'r')
185 settings
= f
.readline()
188 settings
= ast
.literal_eval(settings
)
191 if type(settings
['attractUp']) == float:
192 atr
= settings
['attractUp']
193 settings
['attractUp'] = [0, 0, atr
, atr
]
195 # use old leaf rotations
196 if 'leafDownAngle' not in settings
:
197 l
= settings
['levels']
198 settings
['leafDownAngle'] = settings
['downAngle'][min(l
, 3)]
199 settings
['leafDownAngleV'] = settings
['downAngleV'][min(l
, 3)]
200 settings
['leafRotate'] = settings
['rotate'][min(l
, 3)]
201 settings
['leafRotateV'] = settings
['rotateV'][min(l
, 3)]
206 # Set the flag to use the settings
211 class PresetMenu(Menu
):
212 """Create the preset menu by finding all preset files
213 in the preset directory"""
214 bl_idname
= "SAPLING_MT_preset"
217 def draw(self
, context
):
218 # Get all the sapling presets
219 presets
= [a
for a
in os
.listdir(getPresetpaths()[0]) if a
[-3:] == '.py']
220 presets
.extend([a
for a
in os
.listdir(getPresetpaths()[1]) if a
[-3:] == '.py'])
222 # Append all to the menu
224 layout
.operator("sapling.importdata", text
=p
[:-3]).filename
= p
227 class AddTree(Operator
):
228 bl_idname
= "curve.tree_add"
229 bl_label
= "Sapling: Add Tree"
230 bl_options
= {'REGISTER', 'UNDO'}
232 # Keep the strings in memory, see T83360.
233 _objectList_static_strings
= []
235 def objectList(self
, context
):
236 objects
= AddTree
._objectList
_static
_strings
239 for obj
in bpy
.data
.objects
:
240 if (obj
.type in {'MESH', 'CURVE', 'SURFACE'}) and (obj
.name
not in {'tree', 'leaves'}):
241 objects
.append((obj
.name
, obj
.name
, ""))
244 objects
.append(('NONE', "No objects", "No appropriate objects in the Scene"))
248 def update_tree(self
, context
):
249 self
.do_update
= True
251 def update_leaves(self
, context
):
253 self
.do_update
= True
255 self
.do_update
= False
257 def no_update_tree(self
, context
):
258 self
.do_update
= False
260 do_update
: BoolProperty(
262 default
=True, options
={'HIDDEN'}
264 chooseSet
: EnumProperty(
266 description
='Choose the settings to modify',
268 default
='0', update
=no_update_tree
272 description
='Whether the curve is beveled',
273 default
=False, update
=update_tree
277 description
='Whether the tree is pruned',
278 default
=False, update
=update_tree
280 showLeaves
: BoolProperty(
282 description
='Whether the leaves are shown',
283 default
=False, update
=update_tree
285 useArm
: BoolProperty(
287 description
='Whether the armature is generated',
288 default
=False, update
=update_tree
292 description
='The seed of the random number generator',
293 default
=0, update
=update_tree
295 handleType
: IntProperty(
297 description
='The type of curve handles',
300 default
=0, update
=update_tree
304 description
='Number of recursive branches (Levels)',
308 default
=3, update
=update_tree
310 length
: FloatVectorProperty(
312 description
='The relative lengths of each branch level (nLength)',
314 default
=[1, 0.3, 0.6, 0.45],
315 size
=4, update
=update_tree
317 lengthV
: FloatVectorProperty(
318 name
='Length Variation',
319 description
='The relative length variations of each level (nLengthV)',
322 default
=[0, 0, 0, 0],
323 size
=4, update
=update_tree
325 taperCrown
: FloatProperty(
327 description
='Shorten trunk splits toward outside of tree',
330 default
=0, update
=update_tree
332 branches
: IntVectorProperty(
334 description
='The number of branches grown at each level (nBranches)',
336 default
=[50, 30, 10, 10],
337 size
=4, update
=update_tree
339 curveRes
: IntVectorProperty(
340 name
='Curve Resolution',
341 description
='The number of segments on each branch (nCurveRes)',
343 default
=[3, 5, 3, 1],
344 size
=4, update
=update_tree
346 curve
: FloatVectorProperty(
348 description
='The angle of the end of the branch (nCurve)',
349 default
=[0, -40, -40, 0],
350 size
=4, update
=update_tree
352 curveV
: FloatVectorProperty(
353 name
='Curvature Variation',
354 description
='Variation of the curvature (nCurveV)',
355 default
=[20, 50, 75, 0],
356 size
=4, update
=update_tree
358 curveBack
: FloatVectorProperty(
359 name
='Back Curvature',
360 description
='Curvature for the second half of a branch (nCurveBack)',
361 default
=[0, 0, 0, 0],
362 size
=4, update
=update_tree
364 baseSplits
: IntProperty(
366 description
='Number of trunk splits at its base (nBaseSplits)',
368 default
=0, update
=update_tree
370 segSplits
: FloatVectorProperty(
371 name
='Segment Splits',
372 description
='Number of splits per segment (nSegSplits)',
375 default
=[0, 0, 0, 0],
376 size
=4, update
=update_tree
378 splitByLen
: BoolProperty(
379 name
='Split relative to length',
380 description
='Split proportional to branch length',
381 default
=False, update
=update_tree
384 name
="", # "Branching Mode"
385 description
='Branching and Rotation Mode',
387 default
="rotate", update
=update_tree
389 splitAngle
: FloatVectorProperty(
391 description
='Angle of branch splitting (nSplitAngle)',
392 default
=[0, 0, 0, 0],
393 size
=4, update
=update_tree
395 splitAngleV
: FloatVectorProperty(
396 name
='Split Angle Variation',
397 description
='Variation in the split angle (nSplitAngleV)',
398 default
=[0, 0, 0, 0],
399 size
=4, update
=update_tree
401 scale
: FloatProperty(
403 description
='The tree scale (Scale)',
405 default
=13.0, update
=update_tree
)
406 scaleV
: FloatProperty(name
='Scale Variation',
407 description
='The variation in the tree scale (ScaleV)',
408 default
=3.0, update
=update_tree
410 attractUp
: FloatVectorProperty(
411 name
='Vertical Attraction',
412 description
='Branch upward attraction',
413 default
=[0, 0, 0, 0],
414 size
=4, update
=update_tree
416 attractOut
: FloatVectorProperty(
417 name
='Outward Attraction',
418 description
='Branch outward attraction',
419 default
=[0, 0, 0, 0],
422 size
=4, update
=update_tree
426 description
='The overall shape of the tree (Shape)',
428 default
='7', update
=update_tree
430 shapeS
: EnumProperty(
431 name
='Secondary Branches Shape',
432 description
='The shape of secondary splits',
434 default
='4', update
=update_tree
436 customShape
: FloatVectorProperty(
438 description
='custom shape branch length at (Base, Middle, Middle Position, Top)',
442 default
=[.5, 1.0, .3, .5], update
=update_tree
444 branchDist
: FloatProperty(
445 name
='Branch Distribution',
446 description
='Adjust branch spacing to put more branches at the top or bottom of the tree',
449 default
=1.0, update
=update_tree
453 description
='grow branches in rings',
455 default
=0, update
=update_tree
457 baseSize
: FloatProperty(
459 description
='Fraction of tree height with no branches (Base Size)',
462 default
=0.4, update
=update_tree
464 baseSize_s
: FloatProperty(
465 name
='Secondary Base Size',
466 description
='Factor to decrease base size for each level',
469 default
=0.25, update
=update_tree
471 splitHeight
: FloatProperty(
473 description
='Fraction of tree height with no splits',
476 default
=0.2, update
=update_tree
478 splitBias
: FloatProperty(
480 description
='Put more splits at the top or bottom of the tree',
483 default
=0.0, update
=update_tree
485 ratio
: FloatProperty(
487 description
='Base radius size (Ratio)',
489 default
=0.015, update
=update_tree
491 minRadius
: FloatProperty(
492 name
='Minimum Radius',
493 description
='Minimum branch Radius',
495 default
=0.0, update
=update_tree
497 closeTip
: BoolProperty(
499 description
='Set radius at branch tips to zero',
500 default
=False, update
=update_tree
502 rootFlare
: FloatProperty(
504 description
='Root radius factor',
506 default
=1.0, update
=update_tree
508 autoTaper
: BoolProperty(
510 description
='Calculate taper automatically based on branch lengths',
511 default
=True, update
=update_tree
513 taper
: FloatVectorProperty(
515 description
='The fraction of tapering on each branch (nTaper)',
518 default
=[1, 1, 1, 1],
519 size
=4, update
=update_tree
521 radiusTweak
: FloatVectorProperty(
523 description
='multiply radius by this factor',
526 default
=[1, 1, 1, 1],
527 size
=4, update
=update_tree
529 ratioPower
: FloatProperty(
530 name
='Branch Radius Ratio',
531 description
=('Power which defines the radius of a branch compared to '
532 'the radius of the branch it grew from (RatioPower)'),
534 default
=1.2, update
=update_tree
536 downAngle
: FloatVectorProperty(
538 description
=('The angle between a new branch and the one it grew '
539 'from (nDownAngle)'),
540 default
=[90, 60, 45, 45],
541 size
=4, update
=update_tree
543 downAngleV
: FloatVectorProperty(
544 name
='Down Angle Variation',
545 description
="Angle to decrease Down Angle by towards end of parent branch "
546 "(negative values add random variation)",
547 default
=[0, -50, 10, 10],
548 size
=4, update
=update_tree
550 useOldDownAngle
: BoolProperty(
551 name
='Use old down angle variation',
552 default
=False, update
=update_tree
554 useParentAngle
: BoolProperty(
555 name
='Use parent angle',
556 description
='(first level) Rotate branch to match parent branch',
557 default
=True, update
=update_tree
559 rotate
: FloatVectorProperty(
561 description
="The angle of a new branch around the one it grew from "
562 "(negative values rotate opposite from the previous)",
563 default
=[137.5, 137.5, 137.5, 137.5],
564 size
=4, update
=update_tree
566 rotateV
: FloatVectorProperty(
567 name
='Rotate Angle Variation',
568 description
='Variation in the rotate angle (nRotateV)',
569 default
=[0, 0, 0, 0],
570 size
=4, update
=update_tree
572 scale0
: FloatProperty(
574 description
='The scale of the trunk radius (0Scale)',
576 default
=1.0, update
=update_tree
578 scaleV0
: FloatProperty(
579 name
='Radius Scale Variation',
580 description
='Variation in the radius scale (0ScaleV)',
583 default
=0.2, update
=update_tree
585 pruneWidth
: FloatProperty(
587 description
='The width of the envelope (PruneWidth)',
589 default
=0.4, update
=update_tree
591 pruneBase
: FloatProperty(
592 name
='Prune Base Height',
593 description
='The height of the base of the envelope, bound by trunk height',
596 default
=0.3, update
=update_tree
598 pruneWidthPeak
: FloatProperty(
599 name
='Prune Width Peak',
600 description
=("Fraction of envelope height where the maximum width "
601 "occurs (PruneWidthPeak)"),
603 default
=0.6, update
=update_tree
605 prunePowerHigh
: FloatProperty(
606 name
='Prune Power High',
607 description
=('Power which determines the shape of the upper portion '
608 'of the envelope (PrunePowerHigh)'),
609 default
=0.5, update
=update_tree
611 prunePowerLow
: FloatProperty(
612 name
='Prune Power Low',
613 description
=('Power which determines the shape of the lower portion '
614 'of the envelope (PrunePowerLow)'),
615 default
=0.001, update
=update_tree
617 pruneRatio
: FloatProperty(
619 description
='Proportion of pruned length (PruneRatio)',
622 default
=1.0, update
=update_tree
626 description
="Maximum number of leaves per branch (negative values grow "
627 "leaves from branch tip (palmate compound leaves))",
628 default
=25, update
=update_tree
630 leafDownAngle
: FloatProperty(
631 name
='Leaf Down Angle',
632 description
='The angle between a new leaf and the branch it grew from',
633 default
=45, update
=update_leaves
635 leafDownAngleV
: FloatProperty(
636 name
='Leaf Down Angle Variation',
637 description
="Angle to decrease Down Angle by towards end of parent branch "
638 "(negative values add random variation)",
639 default
=10, update
=update_tree
641 leafRotate
: FloatProperty(
642 name
='Leaf Rotate Angle',
643 description
="The angle of a new leaf around the one it grew from "
644 "(negative values rotate opposite from previous)",
645 default
=137.5, update
=update_tree
647 leafRotateV
: FloatProperty(
648 name
='Leaf Rotate Angle Variation',
649 description
='Variation in the rotate angle',
650 default
=0.0, update
=update_leaves
652 leafScale
: FloatProperty(
654 description
='The scaling applied to the whole leaf (LeafScale)',
656 default
=0.17, update
=update_leaves
658 leafScaleX
: FloatProperty(
660 description
=('The scaling applied to the x direction of the leaf '
663 default
=1.0, update
=update_leaves
665 leafScaleT
: FloatProperty(
666 name
='Leaf Scale Taper',
667 description
='scale leaves toward the tip or base of the patent branch',
670 default
=0.0, update
=update_leaves
672 leafScaleV
: FloatProperty(
673 name
='Leaf Scale Variation',
674 description
='randomize leaf scale',
677 default
=0.0, update
=update_leaves
679 leafShape
: EnumProperty(
681 description
='The shape of the leaves',
682 items
=(('hex', 'Hexagonal', '0'), ('rect', 'Rectangular', '1'),
683 ('dFace', 'DupliFaces', '2'), ('dVert', 'DupliVerts', '3')),
684 default
='hex', update
=update_leaves
686 leafDupliObj
: EnumProperty(
688 description
='Object to use for leaf instancing if Leaf Shape is DupliFaces or DupliVerts',
694 description
='The proportion of bending applied to the leaf (Bend)',
697 default
=0.0, update
=update_leaves
699 leafangle
: FloatProperty(
701 description
='Leaf vertical attraction',
702 default
=0.0, update
=update_leaves
704 horzLeaves
: BoolProperty(
705 name
='Horizontal leaves',
706 description
='Leaves face upwards',
707 default
=True, update
=update_leaves
709 leafDist
: EnumProperty(
710 name
='Leaf Distribution',
711 description
='The way leaves are distributed on branches',
713 default
='6', update
=update_tree
715 bevelRes
: IntProperty(
716 name
='Bevel Resolution',
717 description
='The bevel resolution of the curves',
720 default
=0, update
=update_tree
723 name
='Curve Resolution',
724 description
='The resolution along the curves',
726 default
=4, update
=update_tree
728 handleType
: EnumProperty(
730 description
='The type of handles used in the spline',
732 default
='0', update
=update_tree
734 armAnim
: BoolProperty(
735 name
='Armature Animation',
736 description
='Whether animation is added to the armature',
737 default
=False, update
=update_tree
739 previewArm
: BoolProperty(
741 description
='Disable armature modifier, hide tree, and set bone display to wire, for fast playback',
742 # Disable skin modifier and hide tree and armature, for fast playback
743 default
=False, update
=update_tree
745 leafAnim
: BoolProperty(
746 name
='Leaf Animation',
747 description
='Whether animation is added to the leaves',
748 default
=False, update
=update_tree
750 frameRate
: FloatProperty(
751 name
='Animation Speed',
752 description
=('Adjust speed of animation, relative to scene frame rate'),
754 default
=1, update
=update_tree
756 loopFrames
: IntProperty(
758 description
='Number of frames to make the animation loop for, zero is disabled',
760 default
=0, update
=update_tree
763 windSpeed = FloatProperty(
765 description='The wind speed to apply to the armature',
766 default=2.0, update=update_tree
768 windGust = FloatProperty(
770 description='The greatest increase over Wind Speed',
771 default=0.0, update=update_tree
775 name
='Overall Wind Strength',
776 description
='The intensity of the wind to apply to the armature',
777 default
=1.0, update
=update_tree
780 name
='Wind Gust Strength',
781 description
='The amount of directional movement, (from the positive Y direction)',
782 default
=1.0, update
=update_tree
784 gustF
: FloatProperty(
785 name
='Wind Gust Fequency',
786 description
='The Frequency of directional movement',
787 default
=0.075, update
=update_tree
791 description
='Multiplier for noise amplitude',
792 default
=1.0, update
=update_tree
796 description
='Multiplier for noise fequency',
797 default
=1.0, update
=update_tree
801 description
='Random offset in noise',
802 default
=4.0, update
=update_tree
804 makeMesh
: BoolProperty(
806 description
='Convert curves to mesh, uses skin modifier, enables armature simplification',
807 default
=False, update
=update_tree
809 armLevels
: IntProperty(
810 name
='Armature Levels',
811 description
='Number of branching levels to make bones for, 0 is all levels',
813 default
=2, update
=update_tree
815 boneStep
: IntVectorProperty(
817 description
='Number of stem segments per bone',
819 default
=[1, 1, 1, 1],
820 size
=4, update
=update_tree
822 presetName
: StringProperty(
824 description
='The name of the preset to be saved',
826 subtype
='FILE_NAME', update
=no_update_tree
828 limitImport
: BoolProperty(
830 description
='Limited imported tree to 2 levels & no leaves for speed',
831 default
=True, update
=no_update_tree
833 overwrite
: BoolProperty(
835 description
='When checked, overwrite existing preset files when saving',
836 default
=False, update
=no_update_tree
839 startCurv = FloatProperty(
840 name='Trunk Starting Angle',
841 description=('The angle between vertical and the starting direction'
845 default=0.0, update=update_tree
850 def poll(cls
, context
):
851 return context
.mode
== 'OBJECT'
853 def draw(self
, context
):
857 # layout.label(text='Tree Definition')
859 layout
.prop(self
, 'chooseSet')
861 if self
.chooseSet
== '0':
863 box
.label(text
="Geometry:")
864 box
.prop(self
, 'bevel')
867 row
.prop(self
, 'bevelRes')
868 row
.prop(self
, 'resU')
870 box
.prop(self
, 'handleType')
871 box
.prop(self
, 'shape')
874 col
.prop(self
, 'customShape')
877 box
.prop(self
, 'shapeS')
878 box
.prop(self
, 'branchDist')
879 box
.prop(self
, 'nrings')
880 box
.prop(self
, 'seed')
882 box
.label(text
="Tree Scale:")
884 row
.prop(self
, 'scale')
885 row
.prop(self
, 'scaleV')
887 # Here we create a dict of all the properties.
888 # Unfortunately as_keyword doesn't work with vector properties,
889 # so we need something custom. This is it
891 for a
, b
in (self
.as_keywords(
892 ignore
=("chooseSet", "presetName", "limitImport",
893 "do_update", "overwrite", "leafDupliObj"))).items():
894 # If the property is a vector property then add the slice to the list
897 data
.append((a
, b
[:]))
898 # Otherwise, it is fine so just add it
901 # Create the dict from the list
905 row
.prop(self
, 'presetName')
906 # Send the data dict and the file name to the exporter
907 row
.operator('sapling.exportdata').data
= repr([repr(data
), self
.presetName
, self
.overwrite
])
910 row
.prop(self
, 'overwrite')
912 row
.menu('SAPLING_MT_preset', text
='Load Preset')
913 row
.prop(self
, 'limitImport')
915 elif self
.chooseSet
== '1':
917 box
.label(text
="Branch Radius:")
920 row
.prop(self
, 'bevel')
921 row
.prop(self
, 'bevelRes')
923 box
.prop(self
, 'ratio')
925 row
.prop(self
, 'scale0')
926 row
.prop(self
, 'scaleV0')
927 box
.prop(self
, 'ratioPower')
929 box
.prop(self
, 'minRadius')
930 box
.prop(self
, 'closeTip')
931 box
.prop(self
, 'rootFlare')
933 box
.prop(self
, 'autoTaper')
937 col
.prop(self
, 'taper')
939 col
.prop(self
, 'radiusTweak')
941 elif self
.chooseSet
== '2':
943 box
.label(text
="Branch Splitting:")
944 box
.prop(self
, 'levels')
945 box
.prop(self
, 'baseSplits')
947 row
.prop(self
, 'baseSize')
948 row
.prop(self
, 'baseSize_s')
949 box
.prop(self
, 'splitHeight')
950 box
.prop(self
, 'splitBias')
951 box
.prop(self
, 'splitByLen')
956 col
.prop(self
, 'branches')
957 col
.prop(self
, 'splitAngle')
958 col
.prop(self
, 'rotate')
959 col
.prop(self
, 'attractOut')
962 col
.prop(self
, 'segSplits')
963 col
.prop(self
, 'splitAngleV')
964 col
.prop(self
, 'rotateV')
966 col
.label(text
="Branching Mode:")
967 col
.prop(self
, 'rMode')
969 box
.column().prop(self
, 'curveRes')
971 elif self
.chooseSet
== '3':
973 box
.label(text
="Branch Growth:")
975 box
.prop(self
, 'taperCrown')
980 col
.prop(self
, 'length')
981 col
.prop(self
, 'downAngle')
982 col
.prop(self
, 'curve')
983 col
.prop(self
, 'curveBack')
986 col
.prop(self
, 'lengthV')
987 col
.prop(self
, 'downAngleV')
988 col
.prop(self
, 'curveV')
989 col
.prop(self
, 'attractUp')
991 box
.prop(self
, 'useOldDownAngle')
992 box
.prop(self
, 'useParentAngle')
994 elif self
.chooseSet
== '4':
996 box
.label(text
="Prune:")
997 box
.prop(self
, 'prune')
998 box
.prop(self
, 'pruneRatio')
1000 row
.prop(self
, 'pruneWidth')
1001 row
.prop(self
, 'pruneBase')
1002 box
.prop(self
, 'pruneWidthPeak')
1005 row
.prop(self
, 'prunePowerHigh')
1006 row
.prop(self
, 'prunePowerLow')
1008 elif self
.chooseSet
== '5':
1010 box
.label(text
="Leaves:")
1011 box
.prop(self
, 'showLeaves')
1012 box
.prop(self
, 'leafShape')
1013 box
.prop(self
, 'leafDupliObj')
1014 box
.prop(self
, 'leaves')
1015 box
.prop(self
, 'leafDist')
1019 row
.prop(self
, 'leafDownAngle')
1020 row
.prop(self
, 'leafDownAngleV')
1023 row
.prop(self
, 'leafRotate')
1024 row
.prop(self
, 'leafRotateV')
1028 row
.prop(self
, 'leafScale')
1029 row
.prop(self
, 'leafScaleX')
1032 row
.prop(self
, 'leafScaleT')
1033 row
.prop(self
, 'leafScaleV')
1035 box
.prop(self
, 'horzLeaves')
1036 box
.prop(self
, 'leafangle')
1038 # box.label(text=" ")
1039 # box.prop(self, 'bend')
1041 elif self
.chooseSet
== '6':
1043 box
.label(text
="Armature:")
1045 row
.prop(self
, 'useArm')
1046 box
.prop(self
, 'makeMesh')
1047 box
.label(text
="Armature Simplification:")
1048 box
.prop(self
, 'armLevels')
1049 box
.prop(self
, 'boneStep')
1051 elif self
.chooseSet
== '7':
1053 box
.label(text
="Finalize All Other Settings First!")
1054 box
.prop(self
, 'armAnim')
1055 box
.prop(self
, 'leafAnim')
1056 box
.prop(self
, 'previewArm')
1057 box
.prop(self
, 'frameRate')
1058 box
.prop(self
, 'loopFrames')
1061 # row.prop(self, 'windSpeed')
1062 # row.prop(self, 'windGust')
1064 box
.label(text
='Wind Settings:')
1065 box
.prop(self
, 'wind')
1067 row
.prop(self
, 'gust')
1068 row
.prop(self
, 'gustF')
1070 box
.label(text
='Leaf Wind Settings:')
1071 box
.prop(self
, 'af1')
1072 box
.prop(self
, 'af2')
1073 box
.prop(self
, 'af3')
1075 def execute(self
, context
):
1076 # Ensure the use of the global variables
1077 global settings
, useSet
1078 start_time
= time
.time()
1080 # If we need to set the properties from a preset then do it here
1082 for a
, b
in settings
.items():
1084 if self
.limitImport
:
1085 setattr(self
, 'levels', min(settings
['levels'], 2))
1086 setattr(self
, 'showLeaves', False)
1088 if not self
.do_update
:
1089 return {'PASS_THROUGH'}
1091 # cProfile.runctx("addTree(self)", globals(), locals())
1092 print("Tree creation in %0.1fs" % (time
.time() - start_time
))
1096 def invoke(self
, context
, event
):
1097 bpy
.ops
.sapling
.importdata(filename
="callistemon.py")
1098 return self
.execute(context
)
1101 def menu_func(self
, context
):
1102 self
.layout
.operator(AddTree
.bl_idname
, text
="Sapling Tree Gen", icon
='CURVE_DATA')
1112 from bpy
.utils
import register_class
1115 bpy
.types
.VIEW3D_MT_curve_add
.append(menu_func
)
1119 from bpy
.utils
import unregister_class
1120 for cls
in reversed(classes
):
1121 unregister_class(cls
)
1122 bpy
.types
.VIEW3D_MT_curve_add
.remove(menu_func
)
1125 if __name__
== "__main__":