sun_position: fix warning from deleted prop in User Preferences
[blender-addons.git] / add_curve_ivygen.py
blobf6f0f8a3405fadccbeaf11b2b8f6be613d28aeb3
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8-80 compliant>
21 bl_info = {
22 "name": "IvyGen",
23 "author": "testscreenings, PKHG, TrumanBlending",
24 "version": (0, 1, 5),
25 "blender": (2, 80, 0),
26 "location": "View3D > Sidebar > Ivy Generator (Create Tab)",
27 "description": "Adds generated ivy to a mesh object starting "
28 "at the 3D cursor",
29 "warning": "",
30 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
31 "add_curve/ivy_gen.html",
32 "category": "Add Curve",
36 import bpy
37 from bpy.types import (
38 Operator,
39 Panel,
40 PropertyGroup,
42 from bpy.props import (
43 BoolProperty,
44 FloatProperty,
45 IntProperty,
46 PointerProperty,
48 from mathutils.bvhtree import BVHTree
49 from mathutils import (
50 Vector,
51 Matrix,
53 from collections import deque
54 from math import (
55 pow, cos,
56 pi, atan2,
58 from random import (
59 random as rand_val,
60 seed as rand_seed,
62 import time
65 def createIvyGeometry(IVY, growLeaves):
66 """Create the curve geometry for IVY"""
67 # Compute the local size and the gauss weight filter
68 # local_ivyBranchSize = IVY.ivyBranchSize # * radius * IVY.ivySize
69 gaussWeight = (1.0, 2.0, 4.0, 7.0, 9.0, 10.0, 9.0, 7.0, 4.0, 2.0, 1.0)
71 # Create a new curve and initialise it
72 curve = bpy.data.curves.new("IVY", type='CURVE')
73 curve.dimensions = '3D'
74 curve.bevel_depth = 1
75 curve.fill_mode = 'FULL'
76 curve.resolution_u = 4
78 if growLeaves:
79 # Create the ivy leaves
80 # Order location of the vertices
81 signList = ((-1.0, +1.0),
82 (+1.0, +1.0),
83 (+1.0, -1.0),
84 (-1.0, -1.0),
87 # Get the local size
88 # local_ivyLeafSize = IVY.ivyLeafSize # * radius * IVY.ivySize
90 # Initialise the vertex and face lists
91 vertList = deque()
93 # Store the methods for faster calling
94 addV = vertList.extend
95 rotMat = Matrix.Rotation
97 # Loop over all roots to generate its nodes
98 for root in IVY.ivyRoots:
99 # Only grow if more than one node
100 numNodes = len(root.ivyNodes)
101 if numNodes > 1:
102 # Calculate the local radius
103 local_ivyBranchRadius = 1.0 / (root.parents + 1) + 1.0
104 prevIvyLength = 1.0 / root.ivyNodes[-1].length
105 splineVerts = [ax for n in root.ivyNodes for ax in n.pos.to_4d()]
107 radiusConstant = local_ivyBranchRadius * IVY.ivyBranchSize
108 splineRadii = [radiusConstant * (1.3 - n.length * prevIvyLength)
109 for n in root.ivyNodes]
111 # Add the poly curve and set coords and radii
112 newSpline = curve.splines.new(type='POLY')
113 newSpline.points.add(len(splineVerts) // 4 - 1)
114 newSpline.points.foreach_set('co', splineVerts)
115 newSpline.points.foreach_set('radius', splineRadii)
117 # Loop over all nodes in the root
118 for i, n in enumerate(root.ivyNodes):
119 for k in range(len(gaussWeight)):
120 idx = max(0, min(i + k - 5, numNodes - 1))
121 n.smoothAdhesionVector += (gaussWeight[k] *
122 root.ivyNodes[idx].adhesionVector)
123 n.smoothAdhesionVector /= 56.0
124 n.adhesionLength = n.smoothAdhesionVector.length
125 n.smoothAdhesionVector.normalize()
127 if growLeaves and (i < numNodes - 1):
128 node = root.ivyNodes[i]
129 nodeNext = root.ivyNodes[i + 1]
131 # Find the weight and normalize the smooth adhesion vector
132 weight = pow(node.length * prevIvyLength, 0.7)
134 # Calculate the ground ivy and the new weight
135 groundIvy = max(0.0, -node.smoothAdhesionVector.z)
136 weight += groundIvy * pow(1 - node.length *
137 prevIvyLength, 2)
139 # Find the alignment weight
140 alignmentWeight = node.adhesionLength
142 # Calculate the needed angles
143 phi = atan2(node.smoothAdhesionVector.y,
144 node.smoothAdhesionVector.x) - pi / 2.0
146 theta = (0.5 *
147 node.smoothAdhesionVector.angle(Vector((0, 0, -1)), 0))
149 # Find the size weight
150 sizeWeight = 1.5 - (cos(2 * pi * weight) * 0.5 + 0.5)
152 # Randomise the angles
153 phi += (rand_val() - 0.5) * (1.3 - alignmentWeight)
154 theta += (rand_val() - 0.5) * (1.1 - alignmentWeight)
156 # Calculate the leaf size an append the face to the list
157 leafSize = IVY.ivyLeafSize * sizeWeight
159 for j in range(10):
160 # Generate the probability
161 probability = rand_val()
163 # If we need to grow a leaf, do so
164 if (probability * weight) > IVY.leafProbability:
166 # Generate the random vector
167 randomVector = Vector((rand_val() - 0.5,
168 rand_val() - 0.5,
169 rand_val() - 0.5,
172 # Find the leaf center
173 center = (node.pos.lerp(nodeNext.pos, j / 10.0) +
174 IVY.ivyLeafSize * randomVector)
176 # For each of the verts, rotate/scale and append
177 basisVecX = Vector((1, 0, 0))
178 basisVecY = Vector((0, 1, 0))
180 horiRot = rotMat(theta, 3, 'X')
181 vertRot = rotMat(phi, 3, 'Z')
183 basisVecX.rotate(horiRot)
184 basisVecY.rotate(horiRot)
186 basisVecX.rotate(vertRot)
187 basisVecY.rotate(vertRot)
189 basisVecX *= leafSize
190 basisVecY *= leafSize
192 addV([k1 * basisVecX + k2 * basisVecY + center for
193 k1, k2 in signList])
195 # Add the object and link to scene
196 newCurve = bpy.data.objects.new("IVY_Curve", curve)
197 bpy.context.collection.objects.link(newCurve)
199 if growLeaves:
200 faceList = [[4 * i + l for l in range(4)] for i in
201 range(len(vertList) // 4)]
203 # Generate the new leaf mesh and link
204 me = bpy.data.meshes.new('IvyLeaf')
205 me.from_pydata(vertList, [], faceList)
206 me.update(calc_edges=True)
207 ob = bpy.data.objects.new('IvyLeaf', me)
208 bpy.context.collection.objects.link(ob)
210 me.uv_layers.new(name="Leaves")
212 # Set the uv texture coords
213 # TODO, this is non-functional, default uvs are ok?
215 for d in tex.data:
216 uv1, uv2, uv3, uv4 = signList
219 ob.parent = newCurve
222 class IvyNode:
223 """ The basic class used for each point on the ivy which is grown."""
224 __slots__ = ('pos', 'primaryDir', 'adhesionVector', 'adhesionLength',
225 'smoothAdhesionVector', 'length', 'floatingLength', 'climb')
227 def __init__(self):
228 self.pos = Vector((0, 0, 0))
229 self.primaryDir = Vector((0, 0, 1))
230 self.adhesionVector = Vector((0, 0, 0))
231 self.smoothAdhesionVector = Vector((0, 0, 0))
232 self.length = 0.0001
233 self.floatingLength = 0.0
234 self.climb = True
237 class IvyRoot:
238 """ The class used to hold all ivy nodes growing from this root point."""
239 __slots__ = ('ivyNodes', 'alive', 'parents')
241 def __init__(self):
242 self.ivyNodes = deque()
243 self.alive = True
244 self.parents = 0
247 class Ivy:
248 """ The class holding all parameters and ivy roots."""
249 __slots__ = ('ivyRoots', 'primaryWeight', 'randomWeight',
250 'gravityWeight', 'adhesionWeight', 'branchingProbability',
251 'leafProbability', 'ivySize', 'ivyLeafSize', 'ivyBranchSize',
252 'maxFloatLength', 'maxAdhesionDistance', 'maxLength')
254 def __init__(self,
255 primaryWeight=0.5,
256 randomWeight=0.2,
257 gravityWeight=1.0,
258 adhesionWeight=0.1,
259 branchingProbability=0.05,
260 leafProbability=0.35,
261 ivySize=0.02,
262 ivyLeafSize=0.02,
263 ivyBranchSize=0.001,
264 maxFloatLength=0.5,
265 maxAdhesionDistance=1.0):
267 self.ivyRoots = deque()
268 self.primaryWeight = primaryWeight
269 self.randomWeight = randomWeight
270 self.gravityWeight = gravityWeight
271 self.adhesionWeight = adhesionWeight
272 self.branchingProbability = 1 - branchingProbability
273 self.leafProbability = 1 - leafProbability
274 self.ivySize = ivySize
275 self.ivyLeafSize = ivyLeafSize
276 self.ivyBranchSize = ivyBranchSize
277 self.maxFloatLength = maxFloatLength
278 self.maxAdhesionDistance = maxAdhesionDistance
279 self.maxLength = 0.0
281 # Normalize all the weights only on initialisation
282 sums = self.primaryWeight + self.randomWeight + self.adhesionWeight
283 self.primaryWeight /= sums
284 self.randomWeight /= sums
285 self.adhesionWeight /= sums
287 def seed(self, seedPos):
288 # Seed the Ivy by making a new root and first node
289 tmpRoot = IvyRoot()
290 tmpIvy = IvyNode()
291 tmpIvy.pos = seedPos
293 tmpRoot.ivyNodes.append(tmpIvy)
294 self.ivyRoots.append(tmpRoot)
296 def grow(self, ob, bvhtree):
297 # Determine the local sizes
298 # local_ivySize = self.ivySize # * radius
299 # local_maxFloatLength = self.maxFloatLength # * radius
300 # local_maxAdhesionDistance = self.maxAdhesionDistance # * radius
302 for root in self.ivyRoots:
303 # Make sure the root is alive, if not, skip
304 if not root.alive:
305 continue
307 # Get the last node in the current root
308 prevIvy = root.ivyNodes[-1]
310 # If the node is floating for too long, kill the root
311 if prevIvy.floatingLength > self.maxFloatLength:
312 root.alive = False
314 # Set the primary direction from the last node
315 primaryVector = prevIvy.primaryDir
317 # Make the random vector and normalize
318 randomVector = Vector((rand_val() - 0.5, rand_val() - 0.5,
319 rand_val() - 0.5)) + Vector((0, 0, 0.2))
320 randomVector.normalize()
322 # Calculate the adhesion vector
323 adhesionVector = adhesion(
324 prevIvy.pos, bvhtree, self.maxAdhesionDistance)
326 # Calculate the growing vector
327 growVector = self.ivySize * (primaryVector * self.primaryWeight +
328 randomVector * self.randomWeight +
329 adhesionVector * self.adhesionWeight)
331 # Find the gravity vector
332 gravityVector = (self.ivySize * self.gravityWeight *
333 Vector((0, 0, -1)))
334 gravityVector *= pow(prevIvy.floatingLength / self.maxFloatLength,
335 0.7)
337 # Determine the new position vector
338 newPos = prevIvy.pos + growVector + gravityVector
340 # Check for collisions with the object
341 climbing, newPos = collision(bvhtree, prevIvy.pos, newPos)
343 # Update the growing vector for any collisions
344 growVector = newPos - prevIvy.pos - gravityVector
345 growVector.normalize()
347 # Create a new IvyNode and set its properties
348 tmpNode = IvyNode()
349 tmpNode.climb = climbing
350 tmpNode.pos = newPos
351 tmpNode.primaryDir = prevIvy.primaryDir.lerp(growVector, 0.5)
352 tmpNode.primaryDir.normalize()
353 tmpNode.adhesionVector = adhesionVector
354 tmpNode.length = prevIvy.length + (newPos - prevIvy.pos).length
356 if tmpNode.length > self.maxLength:
357 self.maxLength = tmpNode.length
359 # If the node isn't climbing, update it's floating length
360 # Otherwise set it to 0
361 if not climbing:
362 tmpNode.floatingLength = prevIvy.floatingLength + (newPos -
363 prevIvy.pos).length
364 else:
365 tmpNode.floatingLength = 0.0
367 root.ivyNodes.append(tmpNode)
369 # Loop through all roots to check if a new root is generated
370 for root in self.ivyRoots:
371 # Check the root is alive and isn't at high level of recursion
372 if (root.parents > 3) or (not root.alive):
373 continue
375 # Check to make sure there's more than 1 node
376 if len(root.ivyNodes) > 1:
377 # Loop through all nodes in root to check if new root is grown
378 for node in root.ivyNodes:
379 # Set the last node of the root and find the weighting
380 prevIvy = root.ivyNodes[-1]
381 weight = 1.0 - (cos(2.0 * pi * node.length /
382 prevIvy.length) * 0.5 + 0.5)
384 probability = rand_val()
386 # Check if a new root is grown and if so, set its values
387 if (probability * weight > self.branchingProbability):
388 tmpNode = IvyNode()
389 tmpNode.pos = node.pos
390 tmpNode.floatingLength = node.floatingLength
392 tmpRoot = IvyRoot()
393 tmpRoot.parents = root.parents + 1
395 tmpRoot.ivyNodes.append(tmpNode)
396 self.ivyRoots.append(tmpRoot)
397 return
400 def adhesion(loc, bvhtree, max_l):
401 # Compute the adhesion vector by finding the nearest point
402 nearest_location, *_ = bvhtree.find_nearest(loc, max_l)
403 adhesion_vector = Vector((0.0, 0.0, 0.0))
404 if nearest_location is not None:
405 # Compute the distance to the nearest point
406 adhesion_vector = nearest_location - loc
407 distance = adhesion_vector.length
408 # If it's less than the maximum allowed and not 0, continue
409 if distance:
410 # Compute the direction vector between the closest point and loc
411 adhesion_vector.normalize()
412 adhesion_vector *= 1.0 - distance / max_l
413 # adhesion_vector *= getFaceWeight(ob.data, nearest_result[3])
414 return adhesion_vector
417 def collision(bvhtree, pos, new_pos):
418 # Check for collision with the object
419 climbing = False
421 corrected_new_pos = new_pos
422 direction = new_pos - pos
424 hit_location, hit_normal, *_ = bvhtree.ray_cast(pos, direction, direction.length)
425 # If there's a collision we need to check it
426 if hit_location is not None:
427 # Check whether the collision is going into the object
428 if direction.dot(hit_normal) < 0.0:
429 reflected_direction = (new_pos - hit_location).reflect(hit_normal)
431 corrected_new_pos = hit_location + reflected_direction
432 climbing = True
434 return climbing, corrected_new_pos
437 def bvhtree_from_object(ob):
438 import bmesh
439 bm = bmesh.new()
441 depsgraph = bpy.context.evaluated_depsgraph_get()
442 ob_eval = ob.evaluated_get(depsgraph)
443 mesh = ob_eval.to_mesh()
444 bm.from_mesh(mesh)
445 bm.transform(ob.matrix_world)
447 bvhtree = BVHTree.FromBMesh(bm)
448 ob_eval.to_mesh_clear()
449 return bvhtree
451 def check_mesh_faces(ob):
452 me = ob.data
453 if len(me.polygons) > 0:
454 return True
456 return False
459 class IvyGen(Operator):
460 bl_idname = "curve.ivy_gen"
461 bl_label = "IvyGen"
462 bl_description = "Generate Ivy on an Mesh Object"
463 bl_options = {'REGISTER', 'UNDO'}
465 updateIvy: BoolProperty(
466 name="Update Ivy",
467 description="Update the Ivy location based on the cursor and Panel settings",
468 default=False
470 defaultIvy: BoolProperty(
471 name="Default Ivy",
472 options={"HIDDEN", "SKIP_SAVE"},
473 default=False
476 @classmethod
477 def poll(self, context):
478 # Check if there's an object and whether it's a mesh
479 ob = context.active_object
480 return ((ob is not None) and
481 (ob.type == 'MESH') and
482 (context.mode == 'OBJECT'))
484 def invoke(self, context, event):
485 self.updateIvy = True
486 return self.execute(context)
488 def execute(self, context):
489 # scene = context.scene
490 ivyProps = context.window_manager.ivy_gen_props
492 if not self.updateIvy:
493 return {'PASS_THROUGH'}
495 # assign the variables, check if it is default
496 # Note: update the values if window_manager props defaults are changed
497 randomSeed = ivyProps.randomSeed if not self.defaultIvy else 0
498 maxTime = ivyProps.maxTime if not self.defaultIvy else 0
499 maxIvyLength = ivyProps.maxIvyLength if not self.defaultIvy else 1.0
500 ivySize = ivyProps.ivySize if not self.defaultIvy else 0.02
501 maxFloatLength = ivyProps.maxFloatLength if not self.defaultIvy else 0.5
502 maxAdhesionDistance = ivyProps.maxAdhesionDistance if not self.defaultIvy else 1.0
503 primaryWeight = ivyProps.primaryWeight if not self.defaultIvy else 0.5
504 randomWeight = ivyProps.randomWeight if not self.defaultIvy else 0.2
505 gravityWeight = ivyProps.gravityWeight if not self.defaultIvy else 1.0
506 adhesionWeight = ivyProps.adhesionWeight if not self.defaultIvy else 0.1
507 branchingProbability = ivyProps.branchingProbability if not self.defaultIvy else 0.05
508 leafProbability = ivyProps.leafProbability if not self.defaultIvy else 0.35
509 ivyBranchSize = ivyProps.ivyBranchSize if not self.defaultIvy else 0.001
510 ivyLeafSize = ivyProps.ivyLeafSize if not self.defaultIvy else 0.02
511 growLeaves = ivyProps.growLeaves if not self.defaultIvy else True
513 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
514 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
516 # Get the selected object
517 ob = context.active_object
518 bvhtree = bvhtree_from_object(ob)
520 # Check if the mesh has at least one polygon since some functions
521 # are expecting them in the object's data (see T51753)
522 check_face = check_mesh_faces(ob)
523 if check_face is False:
524 self.report({'WARNING'},
525 "Mesh Object doesn't have at least one Face. "
526 "Operation Cancelled")
527 return {"CANCELLED"}
529 # Compute bounding sphere radius
530 # radius = computeBoundingSphere(ob) # Not needed anymore
532 # Get the seeding point
533 seedPoint = context.scene.cursor.location
535 # Fix the random seed
536 rand_seed(randomSeed)
538 # Make the new ivy
539 IVY = Ivy(
540 primaryWeight=primaryWeight,
541 randomWeight=randomWeight,
542 gravityWeight=gravityWeight,
543 adhesionWeight=adhesionWeight,
544 branchingProbability=branchingProbability,
545 leafProbability=leafProbability,
546 ivySize=ivySize,
547 ivyLeafSize=ivyLeafSize,
548 ivyBranchSize=ivyBranchSize,
549 maxFloatLength=maxFloatLength,
550 maxAdhesionDistance=maxAdhesionDistance
552 # Generate first root and node
553 IVY.seed(seedPoint)
555 checkTime = False
556 maxLength = maxIvyLength # * radius
558 # If we need to check time set the flag
559 if maxTime != 0.0:
560 checkTime = True
562 t = time.time()
563 startPercent = 0.0
564 checkAliveIter = [True, ]
566 # Grow until 200 roots is reached or backup counter exceeds limit
567 while (any(checkAliveIter) and
568 (IVY.maxLength < maxLength) and
569 (not checkTime or (time.time() - t < maxTime))):
570 # Grow the ivy for this iteration
571 IVY.grow(ob, bvhtree)
573 # Print the proportion of ivy growth to console
574 if (IVY.maxLength / maxLength * 100) > 10 * startPercent // 10:
575 print('%0.2f%% of Ivy nodes have grown' %
576 (IVY.maxLength / maxLength * 100))
577 startPercent += 10
578 if IVY.maxLength / maxLength > 1:
579 print("Halting Growth")
581 # Make an iterator to check if all are alive
582 checkAliveIter = (r.alive for r in IVY.ivyRoots)
584 # Create the curve and leaf geometry
585 createIvyGeometry(IVY, growLeaves)
586 print("Geometry Generation Complete")
588 print("Ivy generated in %0.2f s" % (time.time() - t))
590 self.updateIvy = False
591 self.defaultIvy = False
593 return {'FINISHED'}
595 def draw(self, context):
596 layout = self.layout
598 layout.prop(self, "updateIvy", icon="FILE_REFRESH")
601 class CURVE_PT_IvyGenPanel(Panel):
602 bl_label = "Ivy Generator"
603 bl_idname = "CURVE_PT_IvyGenPanel"
604 bl_space_type = "VIEW_3D"
605 bl_region_type = "UI"
606 bl_category = "Create"
607 bl_context = "objectmode"
608 bl_options = {"DEFAULT_CLOSED"}
610 def draw(self, context):
611 layout = self.layout
612 wm = context.window_manager
613 col = layout.column(align=True)
615 prop_new = col.operator("curve.ivy_gen", text="Add New Ivy", icon="OUTLINER_OB_CURVE")
616 prop_new.defaultIvy = False
617 prop_new.updateIvy = True
619 prop_def = col.operator("curve.ivy_gen", text="Add New Default Ivy", icon="CURVE_DATA")
620 prop_def.defaultIvy = True
621 prop_def.updateIvy = True
623 col = layout.column(align=True)
624 col.label(text="Generation Settings:")
625 col.prop(wm.ivy_gen_props, "randomSeed")
626 col.prop(wm.ivy_gen_props, "maxTime")
628 col = layout.column(align=True)
629 col.label(text="Size Settings:")
630 col.prop(wm.ivy_gen_props, "maxIvyLength")
631 col.prop(wm.ivy_gen_props, "ivySize")
632 col.prop(wm.ivy_gen_props, "maxFloatLength")
633 col.prop(wm.ivy_gen_props, "maxAdhesionDistance")
635 col = layout.column(align=True)
636 col.label(text="Weight Settings:")
637 col.prop(wm.ivy_gen_props, "primaryWeight")
638 col.prop(wm.ivy_gen_props, "randomWeight")
639 col.prop(wm.ivy_gen_props, "gravityWeight")
640 col.prop(wm.ivy_gen_props, "adhesionWeight")
642 col = layout.column(align=True)
643 col.label(text="Branch Settings:")
644 col.prop(wm.ivy_gen_props, "branchingProbability")
645 col.prop(wm.ivy_gen_props, "ivyBranchSize")
647 col = layout.column(align=True)
648 col.prop(wm.ivy_gen_props, "growLeaves")
650 if wm.ivy_gen_props.growLeaves:
651 col = layout.column(align=True)
652 col.label(text="Leaf Settings:")
653 col.prop(wm.ivy_gen_props, "ivyLeafSize")
654 col.prop(wm.ivy_gen_props, "leafProbability")
657 class IvyGenProperties(PropertyGroup):
658 maxIvyLength: FloatProperty(
659 name="Max Ivy Length",
660 description="Maximum ivy length in Blender Units",
661 default=1.0,
662 min=0.0,
663 soft_max=3.0,
664 subtype='DISTANCE',
665 unit='LENGTH'
667 primaryWeight: FloatProperty(
668 name="Primary Weight",
669 description="Weighting given to the current direction",
670 default=0.5,
671 min=0.0,
672 soft_max=1.0
674 randomWeight: FloatProperty(
675 name="Random Weight",
676 description="Weighting given to the random direction",
677 default=0.2,
678 min=0.0,
679 soft_max=1.0
681 gravityWeight: FloatProperty(
682 name="Gravity Weight",
683 description="Weighting given to the gravity direction",
684 default=1.0,
685 min=0.0,
686 soft_max=1.0
688 adhesionWeight: FloatProperty(
689 name="Adhesion Weight",
690 description="Weighting given to the adhesion direction",
691 default=0.1,
692 min=0.0,
693 soft_max=1.0
695 branchingProbability: FloatProperty(
696 name="Branching Probability",
697 description="Probability of a new branch forming",
698 default=0.05,
699 min=0.0,
700 soft_max=1.0
702 leafProbability: FloatProperty(
703 name="Leaf Probability",
704 description="Probability of a leaf forming",
705 default=0.35,
706 min=0.0,
707 soft_max=1.0
709 ivySize: FloatProperty(
710 name="Ivy Size",
711 description="The length of an ivy segment in Blender"
712 " Units",
713 default=0.02,
714 min=0.0,
715 soft_max=1.0,
716 precision=3
718 ivyLeafSize: FloatProperty(
719 name="Ivy Leaf Size",
720 description="The size of the ivy leaves",
721 default=0.02,
722 min=0.0,
723 soft_max=0.5,
724 precision=3
726 ivyBranchSize: FloatProperty(
727 name="Ivy Branch Size",
728 description="The size of the ivy branches",
729 default=0.001,
730 min=0.0,
731 soft_max=0.1,
732 precision=4
734 maxFloatLength: FloatProperty(
735 name="Max Float Length",
736 description="The maximum distance that a branch "
737 "can live while floating",
738 default=0.5,
739 min=0.0,
740 soft_max=1.0
742 maxAdhesionDistance: FloatProperty(
743 name="Max Adhesion Length",
744 description="The maximum distance that a branch "
745 "will feel the effects of adhesion",
746 default=1.0,
747 min=0.0,
748 soft_max=2.0,
749 precision=2
751 randomSeed: IntProperty(
752 name="Random Seed",
753 description="The seed governing random generation",
754 default=0,
755 min=0
757 maxTime: FloatProperty(
758 name="Maximum Time",
759 description="The maximum time to run the generation for "
760 "in seconds generation (0.0 = Disabled)",
761 default=0.0,
762 min=0.0,
763 soft_max=10
765 growLeaves: BoolProperty(
766 name="Grow Leaves",
767 description="Grow leaves or not",
768 default=True
772 classes = (
773 IvyGen,
774 IvyGenProperties,
775 CURVE_PT_IvyGenPanel
779 def register():
780 for cls in classes:
781 bpy.utils.register_class(cls)
783 bpy.types.WindowManager.ivy_gen_props = PointerProperty(
784 type=IvyGenProperties
788 def unregister():
789 del bpy.types.WindowManager.ivy_gen_props
791 for cls in reversed(classes):
792 bpy.utils.unregister_class(cls)
795 if __name__ == "__main__":
796 register()