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 # Find the first non-comment, non-blank line, this must contain preset text (all on one line).
187 if settings
and (not settings
.startswith("#")):
191 settings
= ast
.literal_eval(settings
)
194 if type(settings
['attractUp']) == float:
195 atr
= settings
['attractUp']
196 settings
['attractUp'] = [0, 0, atr
, atr
]
198 # use old leaf rotations
199 if 'leafDownAngle' not in settings
:
200 l
= settings
['levels']
201 settings
['leafDownAngle'] = settings
['downAngle'][min(l
, 3)]
202 settings
['leafDownAngleV'] = settings
['downAngleV'][min(l
, 3)]
203 settings
['leafRotate'] = settings
['rotate'][min(l
, 3)]
204 settings
['leafRotateV'] = settings
['rotateV'][min(l
, 3)]
209 # Set the flag to use the settings
214 class PresetMenu(Menu
):
215 """Create the preset menu by finding all preset files
216 in the preset directory"""
217 bl_idname
= "SAPLING_MT_preset"
220 def draw(self
, context
):
221 # Get all the sapling presets
222 presets
= [a
for a
in os
.listdir(getPresetpaths()[0]) if a
[-3:] == '.py']
223 presets
.extend([a
for a
in os
.listdir(getPresetpaths()[1]) if a
[-3:] == '.py'])
225 # Append all to the menu
227 layout
.operator("sapling.importdata", text
=p
[:-3]).filename
= p
230 class AddTree(Operator
):
231 bl_idname
= "curve.tree_add"
232 bl_label
= "Sapling: Add Tree"
233 bl_options
= {'REGISTER', 'UNDO'}
235 # Keep the strings in memory, see T83360.
236 _objectList_static_strings
= []
238 def objectList(self
, context
):
239 objects
= AddTree
._objectList
_static
_strings
242 for obj
in bpy
.data
.objects
:
243 if (obj
.type in {'MESH', 'CURVE', 'SURFACE'}) and (obj
.name
not in {'tree', 'leaves'}):
244 objects
.append((obj
.name
, obj
.name
, ""))
247 objects
.append(('NONE', "No objects", "No appropriate objects in the Scene"))
251 def update_tree(self
, context
):
252 self
.do_update
= True
254 def update_leaves(self
, context
):
256 self
.do_update
= True
258 self
.do_update
= False
260 def no_update_tree(self
, context
):
261 self
.do_update
= False
263 do_update
: BoolProperty(
265 default
=True, options
={'HIDDEN'}
267 chooseSet
: EnumProperty(
269 description
='Choose the settings to modify',
271 default
='0', update
=no_update_tree
275 description
='Whether the curve is beveled',
276 default
=False, update
=update_tree
280 description
='Whether the tree is pruned',
281 default
=False, update
=update_tree
283 showLeaves
: BoolProperty(
285 description
='Whether the leaves are shown',
286 default
=False, update
=update_tree
288 useArm
: BoolProperty(
290 description
='Whether the armature is generated',
291 default
=False, update
=update_tree
295 description
='The seed of the random number generator',
296 default
=0, update
=update_tree
298 handleType
: IntProperty(
300 description
='The type of curve handles',
303 default
=0, update
=update_tree
307 description
='Number of recursive branches (Levels)',
311 default
=3, update
=update_tree
313 length
: FloatVectorProperty(
315 description
='The relative lengths of each branch level (nLength)',
317 default
=[1, 0.3, 0.6, 0.45],
318 size
=4, update
=update_tree
320 lengthV
: FloatVectorProperty(
321 name
='Length Variation',
322 description
='The relative length variations of each level (nLengthV)',
325 default
=[0, 0, 0, 0],
326 size
=4, update
=update_tree
328 taperCrown
: FloatProperty(
330 description
='Shorten trunk splits toward outside of tree',
333 default
=0, update
=update_tree
335 branches
: IntVectorProperty(
337 description
='The number of branches grown at each level (nBranches)',
339 default
=[50, 30, 10, 10],
340 size
=4, update
=update_tree
342 curveRes
: IntVectorProperty(
343 name
='Curve Resolution',
344 description
='The number of segments on each branch (nCurveRes)',
346 default
=[3, 5, 3, 1],
347 size
=4, update
=update_tree
349 curve
: FloatVectorProperty(
351 description
='The angle of the end of the branch (nCurve)',
352 default
=[0, -40, -40, 0],
353 size
=4, update
=update_tree
355 curveV
: FloatVectorProperty(
356 name
='Curvature Variation',
357 description
='Variation of the curvature (nCurveV)',
358 default
=[20, 50, 75, 0],
359 size
=4, update
=update_tree
361 curveBack
: FloatVectorProperty(
362 name
='Back Curvature',
363 description
='Curvature for the second half of a branch (nCurveBack)',
364 default
=[0, 0, 0, 0],
365 size
=4, update
=update_tree
367 baseSplits
: IntProperty(
369 description
='Number of trunk splits at its base (nBaseSplits)',
371 default
=0, update
=update_tree
373 segSplits
: FloatVectorProperty(
374 name
='Segment Splits',
375 description
='Number of splits per segment (nSegSplits)',
378 default
=[0, 0, 0, 0],
379 size
=4, update
=update_tree
381 splitByLen
: BoolProperty(
382 name
='Split relative to length',
383 description
='Split proportional to branch length',
384 default
=False, update
=update_tree
387 name
="", # "Branching Mode"
388 description
='Branching and Rotation Mode',
390 default
="rotate", update
=update_tree
392 splitAngle
: FloatVectorProperty(
394 description
='Angle of branch splitting (nSplitAngle)',
395 default
=[0, 0, 0, 0],
396 size
=4, update
=update_tree
398 splitAngleV
: FloatVectorProperty(
399 name
='Split Angle Variation',
400 description
='Variation in the split angle (nSplitAngleV)',
401 default
=[0, 0, 0, 0],
402 size
=4, update
=update_tree
404 scale
: FloatProperty(
406 description
='The tree scale (Scale)',
408 default
=13.0, update
=update_tree
)
409 scaleV
: FloatProperty(name
='Scale Variation',
410 description
='The variation in the tree scale (ScaleV)',
411 default
=3.0, update
=update_tree
413 attractUp
: FloatVectorProperty(
414 name
='Vertical Attraction',
415 description
='Branch upward attraction',
416 default
=[0, 0, 0, 0],
417 size
=4, update
=update_tree
419 attractOut
: FloatVectorProperty(
420 name
='Outward Attraction',
421 description
='Branch outward attraction',
422 default
=[0, 0, 0, 0],
425 size
=4, update
=update_tree
429 description
='The overall shape of the tree (Shape)',
431 default
='7', update
=update_tree
433 shapeS
: EnumProperty(
434 name
='Secondary Branches Shape',
435 description
='The shape of secondary splits',
437 default
='4', update
=update_tree
439 customShape
: FloatVectorProperty(
441 description
='custom shape branch length at (Base, Middle, Middle Position, Top)',
445 default
=[.5, 1.0, .3, .5], update
=update_tree
447 branchDist
: FloatProperty(
448 name
='Branch Distribution',
449 description
='Adjust branch spacing to put more branches at the top or bottom of the tree',
452 default
=1.0, update
=update_tree
456 description
='grow branches in rings',
458 default
=0, update
=update_tree
460 baseSize
: FloatProperty(
462 description
='Fraction of tree height with no branches (Base Size)',
465 default
=0.4, update
=update_tree
467 baseSize_s
: FloatProperty(
468 name
='Secondary Base Size',
469 description
='Factor to decrease base size for each level',
472 default
=0.25, update
=update_tree
474 splitHeight
: FloatProperty(
476 description
='Fraction of tree height with no splits',
479 default
=0.2, update
=update_tree
481 splitBias
: FloatProperty(
483 description
='Put more splits at the top or bottom of the tree',
486 default
=0.0, update
=update_tree
488 ratio
: FloatProperty(
490 description
='Base radius size (Ratio)',
492 default
=0.015, update
=update_tree
494 minRadius
: FloatProperty(
495 name
='Minimum Radius',
496 description
='Minimum branch Radius',
498 default
=0.0, update
=update_tree
500 closeTip
: BoolProperty(
502 description
='Set radius at branch tips to zero',
503 default
=False, update
=update_tree
505 rootFlare
: FloatProperty(
507 description
='Root radius factor',
509 default
=1.0, update
=update_tree
511 autoTaper
: BoolProperty(
513 description
='Calculate taper automatically based on branch lengths',
514 default
=True, update
=update_tree
516 taper
: FloatVectorProperty(
518 description
='The fraction of tapering on each branch (nTaper)',
521 default
=[1, 1, 1, 1],
522 size
=4, update
=update_tree
524 radiusTweak
: FloatVectorProperty(
526 description
='multiply radius by this factor',
529 default
=[1, 1, 1, 1],
530 size
=4, update
=update_tree
532 ratioPower
: FloatProperty(
533 name
='Branch Radius Ratio',
534 description
=('Power which defines the radius of a branch compared to '
535 'the radius of the branch it grew from (RatioPower)'),
537 default
=1.2, update
=update_tree
539 downAngle
: FloatVectorProperty(
541 description
=('The angle between a new branch and the one it grew '
542 'from (nDownAngle)'),
543 default
=[90, 60, 45, 45],
544 size
=4, update
=update_tree
546 downAngleV
: FloatVectorProperty(
547 name
='Down Angle Variation',
548 description
="Angle to decrease Down Angle by towards end of parent branch "
549 "(negative values add random variation)",
550 default
=[0, -50, 10, 10],
551 size
=4, update
=update_tree
553 useOldDownAngle
: BoolProperty(
554 name
='Use old down angle variation',
555 default
=False, update
=update_tree
557 useParentAngle
: BoolProperty(
558 name
='Use parent angle',
559 description
='(first level) Rotate branch to match parent branch',
560 default
=True, update
=update_tree
562 rotate
: FloatVectorProperty(
564 description
="The angle of a new branch around the one it grew from "
565 "(negative values rotate opposite from the previous)",
566 default
=[137.5, 137.5, 137.5, 137.5],
567 size
=4, update
=update_tree
569 rotateV
: FloatVectorProperty(
570 name
='Rotate Angle Variation',
571 description
='Variation in the rotate angle (nRotateV)',
572 default
=[0, 0, 0, 0],
573 size
=4, update
=update_tree
575 scale0
: FloatProperty(
577 description
='The scale of the trunk radius (0Scale)',
579 default
=1.0, update
=update_tree
581 scaleV0
: FloatProperty(
582 name
='Radius Scale Variation',
583 description
='Variation in the radius scale (0ScaleV)',
586 default
=0.2, update
=update_tree
588 pruneWidth
: FloatProperty(
590 description
='The width of the envelope (PruneWidth)',
592 default
=0.4, update
=update_tree
594 pruneBase
: FloatProperty(
595 name
='Prune Base Height',
596 description
='The height of the base of the envelope, bound by trunk height',
599 default
=0.3, update
=update_tree
601 pruneWidthPeak
: FloatProperty(
602 name
='Prune Width Peak',
603 description
=("Fraction of envelope height where the maximum width "
604 "occurs (PruneWidthPeak)"),
606 default
=0.6, update
=update_tree
608 prunePowerHigh
: FloatProperty(
609 name
='Prune Power High',
610 description
=('Power which determines the shape of the upper portion '
611 'of the envelope (PrunePowerHigh)'),
612 default
=0.5, update
=update_tree
614 prunePowerLow
: FloatProperty(
615 name
='Prune Power Low',
616 description
=('Power which determines the shape of the lower portion '
617 'of the envelope (PrunePowerLow)'),
618 default
=0.001, update
=update_tree
620 pruneRatio
: FloatProperty(
622 description
='Proportion of pruned length (PruneRatio)',
625 default
=1.0, update
=update_tree
629 description
="Maximum number of leaves per branch (negative values grow "
630 "leaves from branch tip (palmate compound leaves))",
631 default
=25, update
=update_tree
633 leafDownAngle
: FloatProperty(
634 name
='Leaf Down Angle',
635 description
='The angle between a new leaf and the branch it grew from',
636 default
=45, update
=update_leaves
638 leafDownAngleV
: FloatProperty(
639 name
='Leaf Down Angle Variation',
640 description
="Angle to decrease Down Angle by towards end of parent branch "
641 "(negative values add random variation)",
642 default
=10, update
=update_tree
644 leafRotate
: FloatProperty(
645 name
='Leaf Rotate Angle',
646 description
="The angle of a new leaf around the one it grew from "
647 "(negative values rotate opposite from previous)",
648 default
=137.5, update
=update_tree
650 leafRotateV
: FloatProperty(
651 name
='Leaf Rotate Angle Variation',
652 description
='Variation in the rotate angle',
653 default
=0.0, update
=update_leaves
655 leafScale
: FloatProperty(
657 description
='The scaling applied to the whole leaf (LeafScale)',
659 default
=0.17, update
=update_leaves
661 leafScaleX
: FloatProperty(
663 description
=('The scaling applied to the x direction of the leaf '
666 default
=1.0, update
=update_leaves
668 leafScaleT
: FloatProperty(
669 name
='Leaf Scale Taper',
670 description
='scale leaves toward the tip or base of the patent branch',
673 default
=0.0, update
=update_leaves
675 leafScaleV
: FloatProperty(
676 name
='Leaf Scale Variation',
677 description
='randomize leaf scale',
680 default
=0.0, update
=update_leaves
682 leafShape
: EnumProperty(
684 description
='The shape of the leaves',
685 items
=(('hex', 'Hexagonal', '0'), ('rect', 'Rectangular', '1'),
686 ('dFace', 'DupliFaces', '2'), ('dVert', 'DupliVerts', '3')),
687 default
='hex', update
=update_leaves
689 leafDupliObj
: EnumProperty(
691 description
='Object to use for leaf instancing if Leaf Shape is DupliFaces or DupliVerts',
697 description
='The proportion of bending applied to the leaf (Bend)',
700 default
=0.0, update
=update_leaves
702 leafangle
: FloatProperty(
704 description
='Leaf vertical attraction',
705 default
=0.0, update
=update_leaves
707 horzLeaves
: BoolProperty(
708 name
='Horizontal leaves',
709 description
='Leaves face upwards',
710 default
=True, update
=update_leaves
712 leafDist
: EnumProperty(
713 name
='Leaf Distribution',
714 description
='The way leaves are distributed on branches',
716 default
='6', update
=update_tree
718 bevelRes
: IntProperty(
719 name
='Bevel Resolution',
720 description
='The bevel resolution of the curves',
723 default
=0, update
=update_tree
726 name
='Curve Resolution',
727 description
='The resolution along the curves',
729 default
=4, update
=update_tree
731 handleType
: EnumProperty(
733 description
='The type of handles used in the spline',
735 default
='0', update
=update_tree
737 armAnim
: BoolProperty(
738 name
='Armature Animation',
739 description
='Whether animation is added to the armature',
740 default
=False, update
=update_tree
742 previewArm
: BoolProperty(
744 description
='Disable armature modifier, hide tree, and set bone display to wire, for fast playback',
745 # Disable skin modifier and hide tree and armature, for fast playback
746 default
=False, update
=update_tree
748 leafAnim
: BoolProperty(
749 name
='Leaf Animation',
750 description
='Whether animation is added to the leaves',
751 default
=False, update
=update_tree
753 frameRate
: FloatProperty(
754 name
='Animation Speed',
755 description
=('Adjust speed of animation, relative to scene frame rate'),
757 default
=1, update
=update_tree
759 loopFrames
: IntProperty(
761 description
='Number of frames to make the animation loop for, zero is disabled',
763 default
=0, update
=update_tree
766 windSpeed = FloatProperty(
768 description='The wind speed to apply to the armature',
769 default=2.0, update=update_tree
771 windGust = FloatProperty(
773 description='The greatest increase over Wind Speed',
774 default=0.0, update=update_tree
778 name
='Overall Wind Strength',
779 description
='The intensity of the wind to apply to the armature',
780 default
=1.0, update
=update_tree
783 name
='Wind Gust Strength',
784 description
='The amount of directional movement, (from the positive Y direction)',
785 default
=1.0, update
=update_tree
787 gustF
: FloatProperty(
788 name
='Wind Gust Fequency',
789 description
='The Frequency of directional movement',
790 default
=0.075, update
=update_tree
794 description
='Multiplier for noise amplitude',
795 default
=1.0, update
=update_tree
799 description
='Multiplier for noise fequency',
800 default
=1.0, update
=update_tree
804 description
='Random offset in noise',
805 default
=4.0, update
=update_tree
807 makeMesh
: BoolProperty(
809 description
='Convert curves to mesh, uses skin modifier, enables armature simplification',
810 default
=False, update
=update_tree
812 armLevels
: IntProperty(
813 name
='Armature Levels',
814 description
='Number of branching levels to make bones for, 0 is all levels',
816 default
=2, update
=update_tree
818 boneStep
: IntVectorProperty(
820 description
='Number of stem segments per bone',
822 default
=[1, 1, 1, 1],
823 size
=4, update
=update_tree
825 presetName
: StringProperty(
827 description
='The name of the preset to be saved',
829 subtype
='FILE_NAME', update
=no_update_tree
831 limitImport
: BoolProperty(
833 description
='Limited imported tree to 2 levels & no leaves for speed',
834 default
=True, update
=no_update_tree
836 overwrite
: BoolProperty(
838 description
='When checked, overwrite existing preset files when saving',
839 default
=False, update
=no_update_tree
842 startCurv = FloatProperty(
843 name='Trunk Starting Angle',
844 description=('The angle between vertical and the starting direction'
848 default=0.0, update=update_tree
853 def poll(cls
, context
):
854 return context
.mode
== 'OBJECT'
856 def draw(self
, context
):
860 # layout.label(text='Tree Definition')
862 layout
.prop(self
, 'chooseSet')
864 if self
.chooseSet
== '0':
866 box
.label(text
="Geometry:")
867 box
.prop(self
, 'bevel')
870 row
.prop(self
, 'bevelRes')
871 row
.prop(self
, 'resU')
873 box
.prop(self
, 'handleType')
874 box
.prop(self
, 'shape')
877 col
.prop(self
, 'customShape')
880 box
.prop(self
, 'shapeS')
881 box
.prop(self
, 'branchDist')
882 box
.prop(self
, 'nrings')
883 box
.prop(self
, 'seed')
885 box
.label(text
="Tree Scale:")
887 row
.prop(self
, 'scale')
888 row
.prop(self
, 'scaleV')
890 # Here we create a dict of all the properties.
891 # Unfortunately as_keyword doesn't work with vector properties,
892 # so we need something custom. This is it
894 for a
, b
in (self
.as_keywords(
895 ignore
=("chooseSet", "presetName", "limitImport",
896 "do_update", "overwrite", "leafDupliObj"))).items():
897 # If the property is a vector property then add the slice to the list
900 data
.append((a
, b
[:]))
901 # Otherwise, it is fine so just add it
904 # Create the dict from the list
908 row
.prop(self
, 'presetName')
909 # Send the data dict and the file name to the exporter
910 row
.operator('sapling.exportdata').data
= repr([repr(data
), self
.presetName
, self
.overwrite
])
913 row
.prop(self
, 'overwrite')
915 row
.menu('SAPLING_MT_preset', text
='Load Preset')
916 row
.prop(self
, 'limitImport')
918 elif self
.chooseSet
== '1':
920 box
.label(text
="Branch Radius:")
923 row
.prop(self
, 'bevel')
924 row
.prop(self
, 'bevelRes')
926 box
.prop(self
, 'ratio')
928 row
.prop(self
, 'scale0')
929 row
.prop(self
, 'scaleV0')
930 box
.prop(self
, 'ratioPower')
932 box
.prop(self
, 'minRadius')
933 box
.prop(self
, 'closeTip')
934 box
.prop(self
, 'rootFlare')
936 box
.prop(self
, 'autoTaper')
940 col
.prop(self
, 'taper')
942 col
.prop(self
, 'radiusTweak')
944 elif self
.chooseSet
== '2':
946 box
.label(text
="Branch Splitting:")
947 box
.prop(self
, 'levels')
948 box
.prop(self
, 'baseSplits')
950 row
.prop(self
, 'baseSize')
951 row
.prop(self
, 'baseSize_s')
952 box
.prop(self
, 'splitHeight')
953 box
.prop(self
, 'splitBias')
954 box
.prop(self
, 'splitByLen')
959 col
.prop(self
, 'branches')
960 col
.prop(self
, 'splitAngle')
961 col
.prop(self
, 'rotate')
962 col
.prop(self
, 'attractOut')
965 col
.prop(self
, 'segSplits')
966 col
.prop(self
, 'splitAngleV')
967 col
.prop(self
, 'rotateV')
969 col
.label(text
="Branching Mode:")
970 col
.prop(self
, 'rMode')
972 box
.column().prop(self
, 'curveRes')
974 elif self
.chooseSet
== '3':
976 box
.label(text
="Branch Growth:")
978 box
.prop(self
, 'taperCrown')
983 col
.prop(self
, 'length')
984 col
.prop(self
, 'downAngle')
985 col
.prop(self
, 'curve')
986 col
.prop(self
, 'curveBack')
989 col
.prop(self
, 'lengthV')
990 col
.prop(self
, 'downAngleV')
991 col
.prop(self
, 'curveV')
992 col
.prop(self
, 'attractUp')
994 box
.prop(self
, 'useOldDownAngle')
995 box
.prop(self
, 'useParentAngle')
997 elif self
.chooseSet
== '4':
999 box
.label(text
="Prune:")
1000 box
.prop(self
, 'prune')
1001 box
.prop(self
, 'pruneRatio')
1003 row
.prop(self
, 'pruneWidth')
1004 row
.prop(self
, 'pruneBase')
1005 box
.prop(self
, 'pruneWidthPeak')
1008 row
.prop(self
, 'prunePowerHigh')
1009 row
.prop(self
, 'prunePowerLow')
1011 elif self
.chooseSet
== '5':
1013 box
.label(text
="Leaves:")
1014 box
.prop(self
, 'showLeaves')
1015 box
.prop(self
, 'leafShape')
1016 box
.prop(self
, 'leafDupliObj')
1017 box
.prop(self
, 'leaves')
1018 box
.prop(self
, 'leafDist')
1022 row
.prop(self
, 'leafDownAngle')
1023 row
.prop(self
, 'leafDownAngleV')
1026 row
.prop(self
, 'leafRotate')
1027 row
.prop(self
, 'leafRotateV')
1031 row
.prop(self
, 'leafScale')
1032 row
.prop(self
, 'leafScaleX')
1035 row
.prop(self
, 'leafScaleT')
1036 row
.prop(self
, 'leafScaleV')
1038 box
.prop(self
, 'horzLeaves')
1039 box
.prop(self
, 'leafangle')
1041 # box.label(text=" ")
1042 # box.prop(self, 'bend')
1044 elif self
.chooseSet
== '6':
1046 box
.label(text
="Armature:")
1048 row
.prop(self
, 'useArm')
1049 box
.prop(self
, 'makeMesh')
1050 box
.label(text
="Armature Simplification:")
1051 box
.prop(self
, 'armLevels')
1052 box
.prop(self
, 'boneStep')
1054 elif self
.chooseSet
== '7':
1056 box
.label(text
="Finalize All Other Settings First!")
1057 box
.prop(self
, 'armAnim')
1058 box
.prop(self
, 'leafAnim')
1059 box
.prop(self
, 'previewArm')
1060 box
.prop(self
, 'frameRate')
1061 box
.prop(self
, 'loopFrames')
1064 # row.prop(self, 'windSpeed')
1065 # row.prop(self, 'windGust')
1067 box
.label(text
='Wind Settings:')
1068 box
.prop(self
, 'wind')
1070 row
.prop(self
, 'gust')
1071 row
.prop(self
, 'gustF')
1073 box
.label(text
='Leaf Wind Settings:')
1074 box
.prop(self
, 'af1')
1075 box
.prop(self
, 'af2')
1076 box
.prop(self
, 'af3')
1078 def execute(self
, context
):
1079 # Ensure the use of the global variables
1080 global settings
, useSet
1081 start_time
= time
.time()
1083 # If we need to set the properties from a preset then do it here
1085 for a
, b
in settings
.items():
1087 if self
.limitImport
:
1088 setattr(self
, 'levels', min(settings
['levels'], 2))
1089 setattr(self
, 'showLeaves', False)
1091 if not self
.do_update
:
1092 return {'PASS_THROUGH'}
1094 # cProfile.runctx("addTree(self)", globals(), locals())
1095 print("Tree creation in %0.1fs" % (time
.time() - start_time
))
1099 def invoke(self
, context
, event
):
1100 bpy
.ops
.sapling
.importdata(filename
="callistemon.py")
1101 return self
.execute(context
)
1104 def menu_func(self
, context
):
1105 self
.layout
.operator(AddTree
.bl_idname
, text
="Sapling Tree Gen", icon
='CURVE_DATA')
1115 from bpy
.utils
import register_class
1118 bpy
.types
.VIEW3D_MT_curve_add
.append(menu_func
)
1122 from bpy
.utils
import unregister_class
1123 for cls
in reversed(classes
):
1124 unregister_class(cls
)
1125 bpy
.types
.VIEW3D_MT_curve_add
.remove(menu_func
)
1128 if __name__
== "__main__":