1 # SPDX-FileCopyrightText: 2011-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 "name": "Sapling Tree Gen",
7 "author": "Andrew Hale (TrumanBlending), Aaron Buchler, CansecoGPC",
10 "location": "View3D > Add > Curve",
11 "description": ("Adds a parametric tree. The method is presented by "
12 "Jason Weber & Joseph Penn in their paper 'Creation and Rendering of "
14 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/sapling.html",
15 "category": "Add Curve",
20 importlib
.reload(utils
)
22 from add_curve_sapling
import utils
31 from bpy
.types
import (
35 from bpy
.props
import (
47 shapeList
= [('0', 'Conical (0)', 'Shape = 0'),
48 ('1', 'Spherical (1)', 'Shape = 1'),
49 ('2', 'Hemispherical (2)', 'Shape = 2'),
50 ('3', 'Cylindrical (3)', 'Shape = 3'),
51 ('4', 'Tapered Cylindrical (4)', 'Shape = 4'),
52 ('5', 'Flame (5)', 'Shape = 5'),
53 ('6', 'Inverse Conical (6)', 'Shape = 6'),
54 ('7', 'Tend Flame (7)', 'Shape = 7')]
56 shapeList3
= [('0', 'Conical', ''),
57 ('6', 'Inverse Conical', ''),
58 ('1', 'Spherical', ''),
59 ('2', 'Hemispherical', ''),
60 ('3', 'Cylindrical', ''),
61 ('4', 'Tapered Cylindrical', ''),
62 ('10', 'Inverse Tapered Cylindrical', ''),
64 ('7', 'Tend Flame', ''),
65 ('8', 'Custom Shape', '')]
67 shapeList4
= [('0', 'Conical', ''),
68 ('6', 'Inverse Conical', ''),
69 ('1', 'Spherical', ''),
70 ('2', 'Hemispherical', ''),
71 ('3', 'Cylindrical', ''),
72 ('4', 'Tapered Cylindrical', ''),
73 ('10', 'Inverse Tapered Cylindrical', ''),
75 ('7', 'Tend Flame', '')]
77 handleList
= [('0', 'Auto', 'Auto'),
78 ('1', 'Vector', 'Vector')]
80 settings
= [('0', 'Geometry', 'Geometry'),
81 ('1', 'Branch Radius', 'Branch Radius'),
82 ('2', 'Branch Splitting', 'Branch Splitting'),
83 ('3', 'Branch Growth', 'Branch Growth'),
84 ('4', 'Pruning', 'Pruning'),
85 ('5', 'Leaves', 'Leaves'),
86 ('6', 'Armature', 'Armature'),
87 ('7', 'Animation', 'Animation')]
89 branchmodes
= [("original", "Original", "rotate around each branch"),
90 ("rotate", "Rotate", "evenly distribute branches to point outward from center of tree"),
91 ("random", "Random", "choose random point")]
95 """Support user defined scripts directory
96 Find the first occurrence of add_curve_sapling/presets in possible script paths
97 and return it as preset path"""
99 script_file
= os
.path
.realpath(__file__
)
100 directory
= os
.path
.dirname(script_file
)
101 directory
= os
.path
.join(directory
, "presets")
105 def getPresetpaths():
106 """Return paths for both local and user preset folders"""
107 userDir
= os
.path
.join(bpy
.utils
.script_path_user(), 'presets', 'operator', 'add_curve_sapling')
109 if os
.path
.isdir(userDir
):
114 script_file
= os
.path
.realpath(__file__
)
115 directory
= os
.path
.dirname(script_file
)
116 localDir
= os
.path
.join(directory
, "presets")
118 return (localDir
, userDir
)
121 class ExportData(Operator
):
122 """This operator handles writing presets to file"""
123 bl_idname
= 'sapling.exportdata'
124 bl_label
= 'Export Preset'
126 data
: StringProperty()
128 def execute(self
, context
):
129 # Unpack some data from the input
130 data
, filename
, overwrite
= eval(self
.data
)
133 # Check whether the file exists by trying to open it.
134 f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'r')
136 # If it exists then report an error
137 self.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
141 # If it doesn't exist, create the file with the required data
142 f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'w')
149 fpath1
= os
.path
.join(getPresetpaths()[0], filename
+ '.py')
150 fpath2
= os
.path
.join(getPresetpaths()[1], filename
+ '.py')
152 if os
.path
.exists(fpath1
):
153 # If it exists in built-in presets then report an error
154 self
.report({'ERROR_INVALID_INPUT'}, 'Can\'t have same name as built-in preset')
156 elif (not os
.path
.exists(fpath2
)) or (os
.path
.exists(fpath2
) and overwrite
):
157 # if (it does not exist) or (exists and overwrite) then write file
159 # If it doesn't exist, create the file with the required data
160 f
= open(os
.path
.join(getPresetpaths()[1], filename
+ '.py'), 'w')
167 # If it exists then report an error
168 self
.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
172 class ImportData(Operator
):
173 """This operator handles importing existing presets"""
174 bl_idname
= "sapling.importdata"
175 bl_label
= "Import Preset"
177 filename
: StringProperty()
179 def execute(self
, context
):
180 # Make sure the operator knows about the global variables
181 global settings
, useSet
182 # Read the preset data into the global settings
184 f
= open(os
.path
.join(getPresetpaths()[0], self
.filename
), 'r')
185 except (FileNotFoundError
, IOError):
186 f
= open(os
.path
.join(getPresetpaths()[1], self
.filename
), 'r')
187 # Find the first non-comment, non-blank line, this must contain preset text (all on one line).
189 if settings
and (not settings
.isspace()) and (not settings
.startswith("#")):
193 settings
= ast
.literal_eval(settings
)
196 if type(settings
['attractUp']) == float:
197 atr
= settings
['attractUp']
198 settings
['attractUp'] = [0, 0, atr
, atr
]
200 # use old leaf rotations
201 if 'leafDownAngle' not in settings
:
202 l
= settings
['levels']
203 settings
['leafDownAngle'] = settings
['downAngle'][min(l
, 3)]
204 settings
['leafDownAngleV'] = settings
['downAngleV'][min(l
, 3)]
205 settings
['leafRotate'] = settings
['rotate'][min(l
, 3)]
206 settings
['leafRotateV'] = settings
['rotateV'][min(l
, 3)]
211 # Set the flag to use the settings
216 class PresetMenu(Menu
):
217 """Create the preset menu by finding all preset files
218 in the preset directory"""
219 bl_idname
= "SAPLING_MT_preset"
222 def draw(self
, context
):
223 # Get all the sapling presets
224 presets
= [a
for a
in os
.listdir(getPresetpaths()[0]) if a
[-3:] == '.py']
225 presets
.extend([a
for a
in os
.listdir(getPresetpaths()[1]) if a
[-3:] == '.py'])
227 # Append all to the menu
229 layout
.operator("sapling.importdata", text
=p
[:-3]).filename
= p
232 class AddTree(Operator
):
233 bl_idname
= "curve.tree_add"
234 bl_label
= "Sapling: Add Tree"
235 bl_options
= {'REGISTER', 'UNDO'}
237 # Keep the strings in memory, see T83360.
238 _objectList_static_strings
= []
240 def objectList(self
, context
):
241 objects
= AddTree
._objectList
_static
_strings
244 for obj
in bpy
.data
.objects
:
245 if (obj
.type in {'MESH', 'CURVE', 'SURFACE'}) and (obj
.name
not in {'tree', 'leaves'}):
246 objects
.append((obj
.name
, obj
.name
, ""))
249 objects
.append(('NONE', "No objects", "No appropriate objects in the Scene"))
253 def update_tree(self
, context
):
254 self
.do_update
= True
256 def update_leaves(self
, context
):
258 self
.do_update
= True
260 self
.do_update
= False
262 def no_update_tree(self
, context
):
263 self
.do_update
= False
265 do_update
: BoolProperty(
267 default
=True, options
={'HIDDEN'}
269 chooseSet
: EnumProperty(
271 description
='Choose the settings to modify',
273 default
='0', update
=no_update_tree
277 description
='Whether the curve is beveled',
278 default
=False, update
=update_tree
282 description
='Whether the tree is pruned',
283 default
=False, update
=update_tree
285 showLeaves
: BoolProperty(
287 description
='Whether the leaves are shown',
288 default
=False, update
=update_tree
290 useArm
: BoolProperty(
292 description
='Whether the armature is generated',
293 default
=False, update
=update_tree
297 description
='The seed of the random number generator',
298 default
=0, update
=update_tree
300 handleType
: IntProperty(
302 description
='The type of curve handles',
305 default
=0, update
=update_tree
309 description
='Number of recursive branches (Levels)',
313 default
=3, update
=update_tree
315 length
: FloatVectorProperty(
317 description
='The relative lengths of each branch level (nLength)',
319 default
=[1, 0.3, 0.6, 0.45],
320 size
=4, update
=update_tree
322 lengthV
: FloatVectorProperty(
323 name
='Length Variation',
324 description
='The relative length variations of each level (nLengthV)',
327 default
=[0, 0, 0, 0],
328 size
=4, update
=update_tree
330 taperCrown
: FloatProperty(
332 description
='Shorten trunk splits toward outside of tree',
335 default
=0, update
=update_tree
337 branches
: IntVectorProperty(
339 description
='The number of branches grown at each level (nBranches)',
341 default
=[50, 30, 10, 10],
342 size
=4, update
=update_tree
344 curveRes
: IntVectorProperty(
345 name
='Curve Resolution',
346 description
='The number of segments on each branch (nCurveRes)',
348 default
=[3, 5, 3, 1],
349 size
=4, update
=update_tree
351 curve
: FloatVectorProperty(
353 description
='The angle of the end of the branch (nCurve)',
354 default
=[0, -40, -40, 0],
355 size
=4, update
=update_tree
357 curveV
: FloatVectorProperty(
358 name
='Curvature Variation',
359 description
='Variation of the curvature (nCurveV)',
360 default
=[20, 50, 75, 0],
361 size
=4, update
=update_tree
363 curveBack
: FloatVectorProperty(
364 name
='Back Curvature',
365 description
='Curvature for the second half of a branch (nCurveBack)',
366 default
=[0, 0, 0, 0],
367 size
=4, update
=update_tree
369 baseSplits
: IntProperty(
371 description
='Number of trunk splits at its base (nBaseSplits)',
373 default
=0, update
=update_tree
375 segSplits
: FloatVectorProperty(
376 name
='Segment Splits',
377 description
='Number of splits per segment (nSegSplits)',
380 default
=[0, 0, 0, 0],
381 size
=4, update
=update_tree
383 splitByLen
: BoolProperty(
384 name
='Split relative to length',
385 description
='Split proportional to branch length',
386 default
=False, update
=update_tree
389 name
="", # "Branching Mode"
390 description
='Branching and Rotation Mode',
392 default
="rotate", update
=update_tree
394 splitAngle
: FloatVectorProperty(
396 description
='Angle of branch splitting (nSplitAngle)',
397 default
=[0, 0, 0, 0],
398 size
=4, update
=update_tree
400 splitAngleV
: FloatVectorProperty(
401 name
='Split Angle Variation',
402 description
='Variation in the split angle (nSplitAngleV)',
403 default
=[0, 0, 0, 0],
404 size
=4, update
=update_tree
406 scale
: FloatProperty(
408 description
='The tree scale (Scale)',
410 default
=13.0, update
=update_tree
)
411 scaleV
: FloatProperty(name
='Scale Variation',
412 description
='The variation in the tree scale (ScaleV)',
413 default
=3.0, update
=update_tree
415 attractUp
: FloatVectorProperty(
416 name
='Vertical Attraction',
417 description
='Branch upward attraction',
418 default
=[0, 0, 0, 0],
419 size
=4, update
=update_tree
421 attractOut
: FloatVectorProperty(
422 name
='Outward Attraction',
423 description
='Branch outward attraction',
424 default
=[0, 0, 0, 0],
427 size
=4, update
=update_tree
431 description
='The overall shape of the tree (Shape)',
433 default
='7', update
=update_tree
435 shapeS
: EnumProperty(
436 name
='Secondary Branches Shape',
437 description
='The shape of secondary splits',
439 default
='4', update
=update_tree
441 customShape
: FloatVectorProperty(
443 description
='custom shape branch length at (Base, Middle, Middle Position, Top)',
447 default
=[.5, 1.0, .3, .5], update
=update_tree
449 branchDist
: FloatProperty(
450 name
='Branch Distribution',
451 description
='Adjust branch spacing to put more branches at the top or bottom of the tree',
454 default
=1.0, update
=update_tree
458 description
='grow branches in rings',
460 default
=0, update
=update_tree
462 baseSize
: FloatProperty(
464 description
='Fraction of tree height with no branches (Base Size)',
467 default
=0.4, update
=update_tree
469 baseSize_s
: FloatProperty(
470 name
='Secondary Base Size',
471 description
='Factor to decrease base size for each level',
474 default
=0.25, update
=update_tree
476 splitHeight
: FloatProperty(
478 description
='Fraction of tree height with no splits',
481 default
=0.2, update
=update_tree
483 splitBias
: FloatProperty(
485 description
='Put more splits at the top or bottom of the tree',
488 default
=0.0, update
=update_tree
490 ratio
: FloatProperty(
492 description
='Base radius size (Ratio)',
494 default
=0.015, update
=update_tree
496 minRadius
: FloatProperty(
497 name
='Minimum Radius',
498 description
='Minimum branch Radius',
500 default
=0.0, update
=update_tree
502 closeTip
: BoolProperty(
504 description
='Set radius at branch tips to zero',
505 default
=False, update
=update_tree
507 rootFlare
: FloatProperty(
509 description
='Root radius factor',
511 default
=1.0, update
=update_tree
513 autoTaper
: BoolProperty(
515 description
='Calculate taper automatically based on branch lengths',
516 default
=True, update
=update_tree
518 taper
: FloatVectorProperty(
520 description
='The fraction of tapering on each branch (nTaper)',
523 default
=[1, 1, 1, 1],
524 size
=4, update
=update_tree
526 radiusTweak
: FloatVectorProperty(
528 description
='multiply radius by this factor',
531 default
=[1, 1, 1, 1],
532 size
=4, update
=update_tree
534 ratioPower
: FloatProperty(
535 name
='Branch Radius Ratio',
536 description
=('Power which defines the radius of a branch compared to '
537 'the radius of the branch it grew from (RatioPower)'),
539 default
=1.2, update
=update_tree
541 downAngle
: FloatVectorProperty(
543 description
=('The angle between a new branch and the one it grew '
544 'from (nDownAngle)'),
545 default
=[90, 60, 45, 45],
546 size
=4, update
=update_tree
548 downAngleV
: FloatVectorProperty(
549 name
='Down Angle Variation',
550 description
="Angle to decrease Down Angle by towards end of parent branch "
551 "(negative values add random variation)",
552 default
=[0, -50, 10, 10],
553 size
=4, update
=update_tree
555 useOldDownAngle
: BoolProperty(
556 name
='Use old down angle variation',
557 default
=False, update
=update_tree
559 useParentAngle
: BoolProperty(
560 name
='Use parent angle',
561 description
='(first level) Rotate branch to match parent branch',
562 default
=True, update
=update_tree
564 rotate
: FloatVectorProperty(
566 description
="The angle of a new branch around the one it grew from "
567 "(negative values rotate opposite from the previous)",
568 default
=[137.5, 137.5, 137.5, 137.5],
569 size
=4, update
=update_tree
571 rotateV
: FloatVectorProperty(
572 name
='Rotate Angle Variation',
573 description
='Variation in the rotate angle (nRotateV)',
574 default
=[0, 0, 0, 0],
575 size
=4, update
=update_tree
577 scale0
: FloatProperty(
579 description
='The scale of the trunk radius (0Scale)',
581 default
=1.0, update
=update_tree
583 scaleV0
: FloatProperty(
584 name
='Radius Scale Variation',
585 description
='Variation in the radius scale (0ScaleV)',
588 default
=0.2, update
=update_tree
590 pruneWidth
: FloatProperty(
592 description
='The width of the envelope (PruneWidth)',
594 default
=0.4, update
=update_tree
596 pruneBase
: FloatProperty(
597 name
='Prune Base Height',
598 description
='The height of the base of the envelope, bound by trunk height',
601 default
=0.3, update
=update_tree
603 pruneWidthPeak
: FloatProperty(
604 name
='Prune Width Peak',
605 description
=("Fraction of envelope height where the maximum width "
606 "occurs (PruneWidthPeak)"),
608 default
=0.6, update
=update_tree
610 prunePowerHigh
: FloatProperty(
611 name
='Prune Power High',
612 description
=('Power which determines the shape of the upper portion '
613 'of the envelope (PrunePowerHigh)'),
614 default
=0.5, update
=update_tree
616 prunePowerLow
: FloatProperty(
617 name
='Prune Power Low',
618 description
=('Power which determines the shape of the lower portion '
619 'of the envelope (PrunePowerLow)'),
620 default
=0.001, update
=update_tree
622 pruneRatio
: FloatProperty(
624 description
='Proportion of pruned length (PruneRatio)',
627 default
=1.0, update
=update_tree
631 description
="Maximum number of leaves per branch (negative values grow "
632 "leaves from branch tip (palmate compound leaves))",
633 default
=25, update
=update_tree
635 leafDownAngle
: FloatProperty(
636 name
='Leaf Down Angle',
637 description
='The angle between a new leaf and the branch it grew from',
638 default
=45, update
=update_leaves
640 leafDownAngleV
: FloatProperty(
641 name
='Leaf Down Angle Variation',
642 description
="Angle to decrease Down Angle by towards end of parent branch "
643 "(negative values add random variation)",
644 default
=10, update
=update_tree
646 leafRotate
: FloatProperty(
647 name
='Leaf Rotate Angle',
648 description
="The angle of a new leaf around the one it grew from "
649 "(negative values rotate opposite from previous)",
650 default
=137.5, update
=update_tree
652 leafRotateV
: FloatProperty(
653 name
='Leaf Rotate Angle Variation',
654 description
='Variation in the rotate angle',
655 default
=0.0, update
=update_leaves
657 leafScale
: FloatProperty(
659 description
='The scaling applied to the whole leaf (LeafScale)',
661 default
=0.17, update
=update_leaves
663 leafScaleX
: FloatProperty(
665 description
=('The scaling applied to the x direction of the leaf '
668 default
=1.0, update
=update_leaves
670 leafScaleT
: FloatProperty(
671 name
='Leaf Scale Taper',
672 description
='scale leaves toward the tip or base of the patent branch',
675 default
=0.0, update
=update_leaves
677 leafScaleV
: FloatProperty(
678 name
='Leaf Scale Variation',
679 description
='randomize leaf scale',
682 default
=0.0, update
=update_leaves
684 leafShape
: EnumProperty(
686 description
='The shape of the leaves',
687 items
=(('hex', 'Hexagonal', '0'), ('rect', 'Rectangular', '1'),
688 ('dFace', 'DupliFaces', '2'), ('dVert', 'DupliVerts', '3')),
689 default
='hex', update
=update_leaves
691 leafDupliObj
: EnumProperty(
693 description
='Object to use for leaf instancing if Leaf Shape is DupliFaces or DupliVerts',
699 description
='The proportion of bending applied to the leaf (Bend)',
702 default
=0.0, update
=update_leaves
704 leafangle
: FloatProperty(
706 description
='Leaf vertical attraction',
707 default
=0.0, update
=update_leaves
709 horzLeaves
: BoolProperty(
710 name
='Horizontal leaves',
711 description
='Leaves face upwards',
712 default
=True, update
=update_leaves
714 leafDist
: EnumProperty(
715 name
='Leaf Distribution',
716 description
='The way leaves are distributed on branches',
718 default
='6', update
=update_tree
720 bevelRes
: IntProperty(
721 name
='Bevel Resolution',
722 description
='The bevel resolution of the curves',
725 default
=0, update
=update_tree
728 name
='Curve Resolution',
729 description
='The resolution along the curves',
731 default
=4, update
=update_tree
733 handleType
: EnumProperty(
735 description
='The type of handles used in the spline',
737 default
='0', update
=update_tree
739 armAnim
: BoolProperty(
740 name
='Armature Animation',
741 description
='Whether animation is added to the armature',
742 default
=False, update
=update_tree
744 previewArm
: BoolProperty(
746 description
='Disable armature modifier, hide tree, and set bone display to wire, for fast playback',
747 # Disable skin modifier and hide tree and armature, for fast playback
748 default
=False, update
=update_tree
750 leafAnim
: BoolProperty(
751 name
='Leaf Animation',
752 description
='Whether animation is added to the leaves',
753 default
=False, update
=update_tree
755 frameRate
: FloatProperty(
756 name
='Animation Speed',
757 description
=('Adjust speed of animation, relative to scene frame rate'),
759 default
=1, update
=update_tree
761 loopFrames
: IntProperty(
763 description
='Number of frames to make the animation loop for, zero is disabled',
765 default
=0, update
=update_tree
768 windSpeed = FloatProperty(
770 description='The wind speed to apply to the armature',
771 default=2.0, update=update_tree
773 windGust = FloatProperty(
775 description='The greatest increase over Wind Speed',
776 default=0.0, update=update_tree
780 name
='Overall Wind Strength',
781 description
='The intensity of the wind to apply to the armature',
782 default
=1.0, update
=update_tree
785 name
='Wind Gust Strength',
786 description
='The amount of directional movement, (from the positive Y direction)',
787 default
=1.0, update
=update_tree
789 gustF
: FloatProperty(
790 name
='Wind Gust Fequency',
791 description
='The Frequency of directional movement',
792 default
=0.075, update
=update_tree
796 description
='Multiplier for noise amplitude',
797 default
=1.0, update
=update_tree
801 description
='Multiplier for noise fequency',
802 default
=1.0, update
=update_tree
806 description
='Random offset in noise',
807 default
=4.0, update
=update_tree
809 makeMesh
: BoolProperty(
811 description
='Convert curves to mesh, uses skin modifier, enables armature simplification',
812 default
=False, update
=update_tree
814 armLevels
: IntProperty(
815 name
='Armature Levels',
816 description
='Number of branching levels to make bones for, 0 is all levels',
818 default
=2, update
=update_tree
820 boneStep
: IntVectorProperty(
822 description
='Number of stem segments per bone',
824 default
=[1, 1, 1, 1],
825 size
=4, update
=update_tree
827 presetName
: StringProperty(
829 description
='The name of the preset to be saved',
831 subtype
='FILE_NAME', update
=no_update_tree
833 limitImport
: BoolProperty(
835 description
='Limited imported tree to 2 levels & no leaves for speed',
836 default
=True, update
=no_update_tree
838 overwrite
: BoolProperty(
840 description
='When checked, overwrite existing preset files when saving',
841 default
=False, update
=no_update_tree
844 startCurv = FloatProperty(
845 name='Trunk Starting Angle',
846 description=('The angle between vertical and the starting direction'
850 default=0.0, update=update_tree
855 def poll(cls
, context
):
856 return context
.mode
== 'OBJECT'
858 def draw(self
, context
):
862 # layout.label(text='Tree Definition')
864 layout
.prop(self
, 'chooseSet')
866 if self
.chooseSet
== '0':
868 box
.label(text
="Geometry:")
869 box
.prop(self
, 'bevel')
872 row
.prop(self
, 'bevelRes')
873 row
.prop(self
, 'resU')
875 box
.prop(self
, 'handleType')
876 box
.prop(self
, 'shape')
879 col
.prop(self
, 'customShape')
882 box
.prop(self
, 'shapeS')
883 box
.prop(self
, 'branchDist')
884 box
.prop(self
, 'nrings')
885 box
.prop(self
, 'seed')
887 box
.label(text
="Tree Scale:")
889 row
.prop(self
, 'scale')
890 row
.prop(self
, 'scaleV')
892 # Here we create a dict of all the properties.
893 # Unfortunately as_keyword doesn't work with vector properties,
894 # so we need something custom. This is it
896 for a
, b
in (self
.as_keywords(
897 ignore
=("chooseSet", "presetName", "limitImport",
898 "do_update", "overwrite", "leafDupliObj"))).items():
899 # If the property is a vector property then add the slice to the list
902 data
.append((a
, b
[:]))
903 # Otherwise, it is fine so just add it
906 # Create the dict from the list
910 row
.prop(self
, 'presetName')
911 # Send the data dict and the file name to the exporter
912 row
.operator('sapling.exportdata').data
= repr([repr(data
), self
.presetName
, self
.overwrite
])
915 row
.prop(self
, 'overwrite')
917 row
.menu('SAPLING_MT_preset', text
='Load Preset')
918 row
.prop(self
, 'limitImport')
920 elif self
.chooseSet
== '1':
922 box
.label(text
="Branch Radius:")
925 row
.prop(self
, 'bevel')
926 row
.prop(self
, 'bevelRes')
928 box
.prop(self
, 'ratio')
930 row
.prop(self
, 'scale0')
931 row
.prop(self
, 'scaleV0')
932 box
.prop(self
, 'ratioPower')
934 box
.prop(self
, 'minRadius')
935 box
.prop(self
, 'closeTip')
936 box
.prop(self
, 'rootFlare')
938 box
.prop(self
, 'autoTaper')
942 col
.prop(self
, 'taper')
944 col
.prop(self
, 'radiusTweak')
946 elif self
.chooseSet
== '2':
948 box
.label(text
="Branch Splitting:")
949 box
.prop(self
, 'levels')
950 box
.prop(self
, 'baseSplits')
952 row
.prop(self
, 'baseSize')
953 row
.prop(self
, 'baseSize_s')
954 box
.prop(self
, 'splitHeight')
955 box
.prop(self
, 'splitBias')
956 box
.prop(self
, 'splitByLen')
961 col
.prop(self
, 'branches')
962 col
.prop(self
, 'splitAngle')
963 col
.prop(self
, 'rotate')
964 col
.prop(self
, 'attractOut')
967 col
.prop(self
, 'segSplits')
968 col
.prop(self
, 'splitAngleV')
969 col
.prop(self
, 'rotateV')
971 col
.label(text
="Branching Mode:")
972 col
.prop(self
, 'rMode')
974 box
.column().prop(self
, 'curveRes')
976 elif self
.chooseSet
== '3':
978 box
.label(text
="Branch Growth:")
980 box
.prop(self
, 'taperCrown')
985 col
.prop(self
, 'length')
986 col
.prop(self
, 'downAngle')
987 col
.prop(self
, 'curve')
988 col
.prop(self
, 'curveBack')
991 col
.prop(self
, 'lengthV')
992 col
.prop(self
, 'downAngleV')
993 col
.prop(self
, 'curveV')
994 col
.prop(self
, 'attractUp')
996 box
.prop(self
, 'useOldDownAngle')
997 box
.prop(self
, 'useParentAngle')
999 elif self
.chooseSet
== '4':
1001 box
.label(text
="Prune:")
1002 box
.prop(self
, 'prune')
1003 box
.prop(self
, 'pruneRatio')
1005 row
.prop(self
, 'pruneWidth')
1006 row
.prop(self
, 'pruneBase')
1007 box
.prop(self
, 'pruneWidthPeak')
1010 row
.prop(self
, 'prunePowerHigh')
1011 row
.prop(self
, 'prunePowerLow')
1013 elif self
.chooseSet
== '5':
1015 box
.label(text
="Leaves:")
1016 box
.prop(self
, 'showLeaves')
1017 box
.prop(self
, 'leafShape')
1018 box
.prop(self
, 'leafDupliObj')
1019 box
.prop(self
, 'leaves')
1020 box
.prop(self
, 'leafDist')
1024 row
.prop(self
, 'leafDownAngle')
1025 row
.prop(self
, 'leafDownAngleV')
1028 row
.prop(self
, 'leafRotate')
1029 row
.prop(self
, 'leafRotateV')
1033 row
.prop(self
, 'leafScale')
1034 row
.prop(self
, 'leafScaleX')
1037 row
.prop(self
, 'leafScaleT')
1038 row
.prop(self
, 'leafScaleV')
1040 box
.prop(self
, 'horzLeaves')
1041 box
.prop(self
, 'leafangle')
1043 # box.label(text=" ")
1044 # box.prop(self, 'bend')
1046 elif self
.chooseSet
== '6':
1048 box
.label(text
="Armature:")
1050 row
.prop(self
, 'useArm')
1051 box
.prop(self
, 'makeMesh')
1052 box
.label(text
="Armature Simplification:")
1053 box
.prop(self
, 'armLevels')
1054 box
.prop(self
, 'boneStep')
1056 elif self
.chooseSet
== '7':
1058 box
.label(text
="Finalize All Other Settings First!")
1059 box
.prop(self
, 'armAnim')
1060 box
.prop(self
, 'leafAnim')
1061 box
.prop(self
, 'previewArm')
1062 box
.prop(self
, 'frameRate')
1063 box
.prop(self
, 'loopFrames')
1066 # row.prop(self, 'windSpeed')
1067 # row.prop(self, 'windGust')
1069 box
.label(text
='Wind Settings:')
1070 box
.prop(self
, 'wind')
1072 row
.prop(self
, 'gust')
1073 row
.prop(self
, 'gustF')
1075 box
.label(text
='Leaf Wind Settings:')
1076 box
.prop(self
, 'af1')
1077 box
.prop(self
, 'af2')
1078 box
.prop(self
, 'af3')
1080 def execute(self
, context
):
1081 # Ensure the use of the global variables
1082 global settings
, useSet
1083 start_time
= time
.time()
1085 # If we need to set the properties from a preset then do it here
1087 for a
, b
in settings
.items():
1089 if self
.limitImport
:
1090 setattr(self
, 'levels', min(settings
['levels'], 2))
1091 setattr(self
, 'showLeaves', False)
1093 if not self
.do_update
:
1094 return {'PASS_THROUGH'}
1096 # cProfile.runctx("addTree(self)", globals(), locals())
1097 print("Tree creation in %0.1fs" % (time
.time() - start_time
))
1101 def invoke(self
, context
, event
):
1102 bpy
.ops
.sapling
.importdata(filename
="callistemon.py")
1103 return self
.execute(context
)
1106 def menu_func(self
, context
):
1107 self
.layout
.operator(AddTree
.bl_idname
, text
="Sapling Tree Gen", icon
='CURVE_DATA')
1117 from bpy
.utils
import register_class
1120 bpy
.types
.VIEW3D_MT_curve_add
.append(menu_func
)
1124 from bpy
.utils
import unregister_class
1125 for cls
in reversed(classes
):
1126 unregister_class(cls
)
1127 bpy
.types
.VIEW3D_MT_curve_add
.remove(menu_func
)
1130 if __name__
== "__main__":