Pose Library: update for rename of asset_library to asset_library_ref
[blender-addons.git] / add_curve_ivygen.py
blobd67225ebad33ea78ec0c06b8c42e7824edf2b5de
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 "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/ivy_gen.html",
31 "category": "Add Curve",
35 import bpy
36 from bpy.types import (
37 Operator,
38 Panel,
39 PropertyGroup,
41 from bpy.props import (
42 BoolProperty,
43 FloatProperty,
44 IntProperty,
45 PointerProperty,
47 from mathutils.bvhtree import BVHTree
48 from mathutils import (
49 Vector,
50 Matrix,
52 from collections import deque
53 from math import (
54 pow, cos,
55 pi, atan2,
57 from random import (
58 random as rand_val,
59 seed as rand_seed,
61 import time
64 def createIvyGeometry(IVY, growLeaves):
65 """Create the curve geometry for IVY"""
66 # Compute the local size and the gauss weight filter
67 # local_ivyBranchSize = IVY.ivyBranchSize # * radius * IVY.ivySize
68 gaussWeight = (1.0, 2.0, 4.0, 7.0, 9.0, 10.0, 9.0, 7.0, 4.0, 2.0, 1.0)
70 # Create a new curve and initialise it
71 curve = bpy.data.curves.new("IVY", type='CURVE')
72 curve.dimensions = '3D'
73 curve.bevel_depth = 1
74 curve.fill_mode = 'FULL'
75 curve.resolution_u = 4
77 if growLeaves:
78 # Create the ivy leaves
79 # Order location of the vertices
80 signList = ((-1.0, +1.0),
81 (+1.0, +1.0),
82 (+1.0, -1.0),
83 (-1.0, -1.0),
86 # Get the local size
87 # local_ivyLeafSize = IVY.ivyLeafSize # * radius * IVY.ivySize
89 # Initialise the vertex and face lists
90 vertList = deque()
92 # Store the methods for faster calling
93 addV = vertList.extend
94 rotMat = Matrix.Rotation
96 # Loop over all roots to generate its nodes
97 for root in IVY.ivyRoots:
98 # Only grow if more than one node
99 numNodes = len(root.ivyNodes)
100 if numNodes > 1:
101 # Calculate the local radius
102 local_ivyBranchRadius = 1.0 / (root.parents + 1) + 1.0
103 prevIvyLength = 1.0 / root.ivyNodes[-1].length
104 splineVerts = [ax for n in root.ivyNodes for ax in n.pos.to_4d()]
106 radiusConstant = local_ivyBranchRadius * IVY.ivyBranchSize
107 splineRadii = [radiusConstant * (1.3 - n.length * prevIvyLength)
108 for n in root.ivyNodes]
110 # Add the poly curve and set coords and radii
111 newSpline = curve.splines.new(type='POLY')
112 newSpline.points.add(len(splineVerts) // 4 - 1)
113 newSpline.points.foreach_set('co', splineVerts)
114 newSpline.points.foreach_set('radius', splineRadii)
116 # Loop over all nodes in the root
117 for i, n in enumerate(root.ivyNodes):
118 for k in range(len(gaussWeight)):
119 idx = max(0, min(i + k - 5, numNodes - 1))
120 n.smoothAdhesionVector += (gaussWeight[k] *
121 root.ivyNodes[idx].adhesionVector)
122 n.smoothAdhesionVector /= 56.0
123 n.adhesionLength = n.smoothAdhesionVector.length
124 n.smoothAdhesionVector.normalize()
126 if growLeaves and (i < numNodes - 1):
127 node = root.ivyNodes[i]
128 nodeNext = root.ivyNodes[i + 1]
130 # Find the weight and normalize the smooth adhesion vector
131 weight = pow(node.length * prevIvyLength, 0.7)
133 # Calculate the ground ivy and the new weight
134 groundIvy = max(0.0, -node.smoothAdhesionVector.z)
135 weight += groundIvy * pow(1 - node.length *
136 prevIvyLength, 2)
138 # Find the alignment weight
139 alignmentWeight = node.adhesionLength
141 # Calculate the needed angles
142 phi = atan2(node.smoothAdhesionVector.y,
143 node.smoothAdhesionVector.x) - pi / 2.0
145 theta = (0.5 *
146 node.smoothAdhesionVector.angle(Vector((0, 0, -1)), 0))
148 # Find the size weight
149 sizeWeight = 1.5 - (cos(2 * pi * weight) * 0.5 + 0.5)
151 # Randomise the angles
152 phi += (rand_val() - 0.5) * (1.3 - alignmentWeight)
153 theta += (rand_val() - 0.5) * (1.1 - alignmentWeight)
155 # Calculate the leaf size an append the face to the list
156 leafSize = IVY.ivyLeafSize * sizeWeight
158 for j in range(10):
159 # Generate the probability
160 probability = rand_val()
162 # If we need to grow a leaf, do so
163 if (probability * weight) > IVY.leafProbability:
165 # Generate the random vector
166 randomVector = Vector((rand_val() - 0.5,
167 rand_val() - 0.5,
168 rand_val() - 0.5,
171 # Find the leaf center
172 center = (node.pos.lerp(nodeNext.pos, j / 10.0) +
173 IVY.ivyLeafSize * randomVector)
175 # For each of the verts, rotate/scale and append
176 basisVecX = Vector((1, 0, 0))
177 basisVecY = Vector((0, 1, 0))
179 horiRot = rotMat(theta, 3, 'X')
180 vertRot = rotMat(phi, 3, 'Z')
182 basisVecX.rotate(horiRot)
183 basisVecY.rotate(horiRot)
185 basisVecX.rotate(vertRot)
186 basisVecY.rotate(vertRot)
188 basisVecX *= leafSize
189 basisVecY *= leafSize
191 addV([k1 * basisVecX + k2 * basisVecY + center for
192 k1, k2 in signList])
194 # Add the object and link to scene
195 newCurve = bpy.data.objects.new("IVY_Curve", curve)
196 bpy.context.collection.objects.link(newCurve)
198 if growLeaves:
199 faceList = [[4 * i + l for l in range(4)] for i in
200 range(len(vertList) // 4)]
202 # Generate the new leaf mesh and link
203 me = bpy.data.meshes.new('IvyLeaf')
204 me.from_pydata(vertList, [], faceList)
205 me.update(calc_edges=True)
206 ob = bpy.data.objects.new('IvyLeaf', me)
207 bpy.context.collection.objects.link(ob)
209 me.uv_layers.new(name="Leaves")
211 # Set the uv texture coords
212 # TODO, this is non-functional, default uvs are ok?
214 for d in tex.data:
215 uv1, uv2, uv3, uv4 = signList
218 ob.parent = newCurve
221 class IvyNode:
222 """ The basic class used for each point on the ivy which is grown."""
223 __slots__ = ('pos', 'primaryDir', 'adhesionVector', 'adhesionLength',
224 'smoothAdhesionVector', 'length', 'floatingLength', 'climb')
226 def __init__(self):
227 self.pos = Vector((0, 0, 0))
228 self.primaryDir = Vector((0, 0, 1))
229 self.adhesionVector = Vector((0, 0, 0))
230 self.smoothAdhesionVector = Vector((0, 0, 0))
231 self.length = 0.0001
232 self.floatingLength = 0.0
233 self.climb = True
236 class IvyRoot:
237 """ The class used to hold all ivy nodes growing from this root point."""
238 __slots__ = ('ivyNodes', 'alive', 'parents')
240 def __init__(self):
241 self.ivyNodes = deque()
242 self.alive = True
243 self.parents = 0
246 class Ivy:
247 """ The class holding all parameters and ivy roots."""
248 __slots__ = ('ivyRoots', 'primaryWeight', 'randomWeight',
249 'gravityWeight', 'adhesionWeight', 'branchingProbability',
250 'leafProbability', 'ivySize', 'ivyLeafSize', 'ivyBranchSize',
251 'maxFloatLength', 'maxAdhesionDistance', 'maxLength')
253 def __init__(self,
254 primaryWeight=0.5,
255 randomWeight=0.2,
256 gravityWeight=1.0,
257 adhesionWeight=0.1,
258 branchingProbability=0.05,
259 leafProbability=0.35,
260 ivySize=0.02,
261 ivyLeafSize=0.02,
262 ivyBranchSize=0.001,
263 maxFloatLength=0.5,
264 maxAdhesionDistance=1.0):
266 self.ivyRoots = deque()
267 self.primaryWeight = primaryWeight
268 self.randomWeight = randomWeight
269 self.gravityWeight = gravityWeight
270 self.adhesionWeight = adhesionWeight
271 self.branchingProbability = 1 - branchingProbability
272 self.leafProbability = 1 - leafProbability
273 self.ivySize = ivySize
274 self.ivyLeafSize = ivyLeafSize
275 self.ivyBranchSize = ivyBranchSize
276 self.maxFloatLength = maxFloatLength
277 self.maxAdhesionDistance = maxAdhesionDistance
278 self.maxLength = 0.0
280 # Normalize all the weights only on initialisation
281 sums = self.primaryWeight + self.randomWeight + self.adhesionWeight
282 self.primaryWeight /= sums
283 self.randomWeight /= sums
284 self.adhesionWeight /= sums
286 def seed(self, seedPos):
287 # Seed the Ivy by making a new root and first node
288 tmpRoot = IvyRoot()
289 tmpIvy = IvyNode()
290 tmpIvy.pos = seedPos
292 tmpRoot.ivyNodes.append(tmpIvy)
293 self.ivyRoots.append(tmpRoot)
295 def grow(self, ob, bvhtree):
296 # Determine the local sizes
297 # local_ivySize = self.ivySize # * radius
298 # local_maxFloatLength = self.maxFloatLength # * radius
299 # local_maxAdhesionDistance = self.maxAdhesionDistance # * radius
301 for root in self.ivyRoots:
302 # Make sure the root is alive, if not, skip
303 if not root.alive:
304 continue
306 # Get the last node in the current root
307 prevIvy = root.ivyNodes[-1]
309 # If the node is floating for too long, kill the root
310 if prevIvy.floatingLength > self.maxFloatLength:
311 root.alive = False
313 # Set the primary direction from the last node
314 primaryVector = prevIvy.primaryDir
316 # Make the random vector and normalize
317 randomVector = Vector((rand_val() - 0.5, rand_val() - 0.5,
318 rand_val() - 0.5)) + Vector((0, 0, 0.2))
319 randomVector.normalize()
321 # Calculate the adhesion vector
322 adhesionVector = adhesion(
323 prevIvy.pos, bvhtree, self.maxAdhesionDistance)
325 # Calculate the growing vector
326 growVector = self.ivySize * (primaryVector * self.primaryWeight +
327 randomVector * self.randomWeight +
328 adhesionVector * self.adhesionWeight)
330 # Find the gravity vector
331 gravityVector = (self.ivySize * self.gravityWeight *
332 Vector((0, 0, -1)))
333 gravityVector *= pow(prevIvy.floatingLength / self.maxFloatLength,
334 0.7)
336 # Determine the new position vector
337 newPos = prevIvy.pos + growVector + gravityVector
339 # Check for collisions with the object
340 climbing, newPos = collision(bvhtree, prevIvy.pos, newPos)
342 # Update the growing vector for any collisions
343 growVector = newPos - prevIvy.pos - gravityVector
344 growVector.normalize()
346 # Create a new IvyNode and set its properties
347 tmpNode = IvyNode()
348 tmpNode.climb = climbing
349 tmpNode.pos = newPos
350 tmpNode.primaryDir = prevIvy.primaryDir.lerp(growVector, 0.5)
351 tmpNode.primaryDir.normalize()
352 tmpNode.adhesionVector = adhesionVector
353 tmpNode.length = prevIvy.length + (newPos - prevIvy.pos).length
355 if tmpNode.length > self.maxLength:
356 self.maxLength = tmpNode.length
358 # If the node isn't climbing, update it's floating length
359 # Otherwise set it to 0
360 if not climbing:
361 tmpNode.floatingLength = prevIvy.floatingLength + (newPos -
362 prevIvy.pos).length
363 else:
364 tmpNode.floatingLength = 0.0
366 root.ivyNodes.append(tmpNode)
368 # Loop through all roots to check if a new root is generated
369 for root in self.ivyRoots:
370 # Check the root is alive and isn't at high level of recursion
371 if (root.parents > 3) or (not root.alive):
372 continue
374 # Check to make sure there's more than 1 node
375 if len(root.ivyNodes) > 1:
376 # Loop through all nodes in root to check if new root is grown
377 for node in root.ivyNodes:
378 # Set the last node of the root and find the weighting
379 prevIvy = root.ivyNodes[-1]
380 weight = 1.0 - (cos(2.0 * pi * node.length /
381 prevIvy.length) * 0.5 + 0.5)
383 probability = rand_val()
385 # Check if a new root is grown and if so, set its values
386 if (probability * weight > self.branchingProbability):
387 tmpNode = IvyNode()
388 tmpNode.pos = node.pos
389 tmpNode.floatingLength = node.floatingLength
391 tmpRoot = IvyRoot()
392 tmpRoot.parents = root.parents + 1
394 tmpRoot.ivyNodes.append(tmpNode)
395 self.ivyRoots.append(tmpRoot)
396 return
399 def adhesion(loc, bvhtree, max_l):
400 # Compute the adhesion vector by finding the nearest point
401 nearest_location, *_ = bvhtree.find_nearest(loc, max_l)
402 adhesion_vector = Vector((0.0, 0.0, 0.0))
403 if nearest_location is not None:
404 # Compute the distance to the nearest point
405 adhesion_vector = nearest_location - loc
406 distance = adhesion_vector.length
407 # If it's less than the maximum allowed and not 0, continue
408 if distance:
409 # Compute the direction vector between the closest point and loc
410 adhesion_vector.normalize()
411 adhesion_vector *= 1.0 - distance / max_l
412 # adhesion_vector *= getFaceWeight(ob.data, nearest_result[3])
413 return adhesion_vector
416 def collision(bvhtree, pos, new_pos):
417 # Check for collision with the object
418 climbing = False
420 corrected_new_pos = new_pos
421 direction = new_pos - pos
423 hit_location, hit_normal, *_ = bvhtree.ray_cast(pos, direction, direction.length)
424 # If there's a collision we need to check it
425 if hit_location is not None:
426 # Check whether the collision is going into the object
427 if direction.dot(hit_normal) < 0.0:
428 reflected_direction = (new_pos - hit_location).reflect(hit_normal)
430 corrected_new_pos = hit_location + reflected_direction
431 climbing = True
433 return climbing, corrected_new_pos
436 def bvhtree_from_object(ob):
437 import bmesh
438 bm = bmesh.new()
440 depsgraph = bpy.context.evaluated_depsgraph_get()
441 ob_eval = ob.evaluated_get(depsgraph)
442 mesh = ob_eval.to_mesh()
443 bm.from_mesh(mesh)
444 bm.transform(ob.matrix_world)
446 bvhtree = BVHTree.FromBMesh(bm)
447 ob_eval.to_mesh_clear()
448 return bvhtree
450 def check_mesh_faces(ob):
451 me = ob.data
452 if len(me.polygons) > 0:
453 return True
455 return False
458 class IvyGen(Operator):
459 bl_idname = "curve.ivy_gen"
460 bl_label = "IvyGen"
461 bl_description = "Generate Ivy on an Mesh Object"
462 bl_options = {'REGISTER', 'UNDO'}
464 updateIvy: BoolProperty(
465 name="Update Ivy",
466 description="Update the Ivy location based on the cursor and Panel settings",
467 default=False
469 defaultIvy: BoolProperty(
470 name="Default Ivy",
471 options={"HIDDEN", "SKIP_SAVE"},
472 default=False
475 @classmethod
476 def poll(self, context):
477 # Check if there's an object and whether it's a mesh
478 ob = context.active_object
479 return ((ob is not None) and
480 (ob.type == 'MESH') and
481 (context.mode == 'OBJECT'))
483 def invoke(self, context, event):
484 self.updateIvy = True
485 return self.execute(context)
487 def execute(self, context):
488 # scene = context.scene
489 ivyProps = context.window_manager.ivy_gen_props
491 if not self.updateIvy:
492 return {'PASS_THROUGH'}
494 # assign the variables, check if it is default
495 # Note: update the values if window_manager props defaults are changed
496 randomSeed = ivyProps.randomSeed if not self.defaultIvy else 0
497 maxTime = ivyProps.maxTime if not self.defaultIvy else 0
498 maxIvyLength = ivyProps.maxIvyLength if not self.defaultIvy else 1.0
499 ivySize = ivyProps.ivySize if not self.defaultIvy else 0.02
500 maxFloatLength = ivyProps.maxFloatLength if not self.defaultIvy else 0.5
501 maxAdhesionDistance = ivyProps.maxAdhesionDistance if not self.defaultIvy else 1.0
502 primaryWeight = ivyProps.primaryWeight if not self.defaultIvy else 0.5
503 randomWeight = ivyProps.randomWeight if not self.defaultIvy else 0.2
504 gravityWeight = ivyProps.gravityWeight if not self.defaultIvy else 1.0
505 adhesionWeight = ivyProps.adhesionWeight if not self.defaultIvy else 0.1
506 branchingProbability = ivyProps.branchingProbability if not self.defaultIvy else 0.05
507 leafProbability = ivyProps.leafProbability if not self.defaultIvy else 0.35
508 ivyBranchSize = ivyProps.ivyBranchSize if not self.defaultIvy else 0.001
509 ivyLeafSize = ivyProps.ivyLeafSize if not self.defaultIvy else 0.02
510 growLeaves = ivyProps.growLeaves if not self.defaultIvy else True
512 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
513 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
515 # Get the selected object
516 ob = context.active_object
517 bvhtree = bvhtree_from_object(ob)
519 # Check if the mesh has at least one polygon since some functions
520 # are expecting them in the object's data (see T51753)
521 check_face = check_mesh_faces(ob)
522 if check_face is False:
523 self.report({'WARNING'},
524 "Mesh Object doesn't have at least one Face. "
525 "Operation Cancelled")
526 return {"CANCELLED"}
528 # Compute bounding sphere radius
529 # radius = computeBoundingSphere(ob) # Not needed anymore
531 # Get the seeding point
532 seedPoint = context.scene.cursor.location
534 # Fix the random seed
535 rand_seed(randomSeed)
537 # Make the new ivy
538 IVY = Ivy(
539 primaryWeight=primaryWeight,
540 randomWeight=randomWeight,
541 gravityWeight=gravityWeight,
542 adhesionWeight=adhesionWeight,
543 branchingProbability=branchingProbability,
544 leafProbability=leafProbability,
545 ivySize=ivySize,
546 ivyLeafSize=ivyLeafSize,
547 ivyBranchSize=ivyBranchSize,
548 maxFloatLength=maxFloatLength,
549 maxAdhesionDistance=maxAdhesionDistance
551 # Generate first root and node
552 IVY.seed(seedPoint)
554 checkTime = False
555 maxLength = maxIvyLength # * radius
557 # If we need to check time set the flag
558 if maxTime != 0.0:
559 checkTime = True
561 t = time.time()
562 startPercent = 0.0
563 checkAliveIter = [True, ]
565 # Grow until 200 roots is reached or backup counter exceeds limit
566 while (any(checkAliveIter) and
567 (IVY.maxLength < maxLength) and
568 (not checkTime or (time.time() - t < maxTime))):
569 # Grow the ivy for this iteration
570 IVY.grow(ob, bvhtree)
572 # Print the proportion of ivy growth to console
573 if (IVY.maxLength / maxLength * 100) > 10 * startPercent // 10:
574 print('%0.2f%% of Ivy nodes have grown' %
575 (IVY.maxLength / maxLength * 100))
576 startPercent += 10
577 if IVY.maxLength / maxLength > 1:
578 print("Halting Growth")
580 # Make an iterator to check if all are alive
581 checkAliveIter = (r.alive for r in IVY.ivyRoots)
583 # Create the curve and leaf geometry
584 createIvyGeometry(IVY, growLeaves)
585 print("Geometry Generation Complete")
587 print("Ivy generated in %0.2f s" % (time.time() - t))
589 self.updateIvy = False
590 self.defaultIvy = False
592 return {'FINISHED'}
594 def draw(self, context):
595 layout = self.layout
597 layout.prop(self, "updateIvy", icon="FILE_REFRESH")
600 class CURVE_PT_IvyGenPanel(Panel):
601 bl_label = "Ivy Generator"
602 bl_idname = "CURVE_PT_IvyGenPanel"
603 bl_space_type = "VIEW_3D"
604 bl_region_type = "UI"
605 bl_category = "Create"
606 bl_context = "objectmode"
607 bl_options = {"DEFAULT_CLOSED"}
609 def draw(self, context):
610 layout = self.layout
611 wm = context.window_manager
612 col = layout.column(align=True)
614 prop_new = col.operator("curve.ivy_gen", text="Add New Ivy", icon="OUTLINER_OB_CURVE")
615 prop_new.defaultIvy = False
616 prop_new.updateIvy = True
618 prop_def = col.operator("curve.ivy_gen", text="Add New Default Ivy", icon="CURVE_DATA")
619 prop_def.defaultIvy = True
620 prop_def.updateIvy = True
622 col = layout.column(align=True)
623 col.label(text="Generation Settings:")
624 col.prop(wm.ivy_gen_props, "randomSeed")
625 col.prop(wm.ivy_gen_props, "maxTime")
627 col = layout.column(align=True)
628 col.label(text="Size Settings:")
629 col.prop(wm.ivy_gen_props, "maxIvyLength")
630 col.prop(wm.ivy_gen_props, "ivySize")
631 col.prop(wm.ivy_gen_props, "maxFloatLength")
632 col.prop(wm.ivy_gen_props, "maxAdhesionDistance")
634 col = layout.column(align=True)
635 col.label(text="Weight Settings:")
636 col.prop(wm.ivy_gen_props, "primaryWeight")
637 col.prop(wm.ivy_gen_props, "randomWeight")
638 col.prop(wm.ivy_gen_props, "gravityWeight")
639 col.prop(wm.ivy_gen_props, "adhesionWeight")
641 col = layout.column(align=True)
642 col.label(text="Branch Settings:")
643 col.prop(wm.ivy_gen_props, "branchingProbability")
644 col.prop(wm.ivy_gen_props, "ivyBranchSize")
646 col = layout.column(align=True)
647 col.prop(wm.ivy_gen_props, "growLeaves")
649 if wm.ivy_gen_props.growLeaves:
650 col = layout.column(align=True)
651 col.label(text="Leaf Settings:")
652 col.prop(wm.ivy_gen_props, "ivyLeafSize")
653 col.prop(wm.ivy_gen_props, "leafProbability")
656 class IvyGenProperties(PropertyGroup):
657 maxIvyLength: FloatProperty(
658 name="Max Ivy Length",
659 description="Maximum ivy length in Blender Units",
660 default=1.0,
661 min=0.0,
662 soft_max=3.0,
663 subtype='DISTANCE',
664 unit='LENGTH'
666 primaryWeight: FloatProperty(
667 name="Primary Weight",
668 description="Weighting given to the current direction",
669 default=0.5,
670 min=0.0,
671 soft_max=1.0
673 randomWeight: FloatProperty(
674 name="Random Weight",
675 description="Weighting given to the random direction",
676 default=0.2,
677 min=0.0,
678 soft_max=1.0
680 gravityWeight: FloatProperty(
681 name="Gravity Weight",
682 description="Weighting given to the gravity direction",
683 default=1.0,
684 min=0.0,
685 soft_max=1.0
687 adhesionWeight: FloatProperty(
688 name="Adhesion Weight",
689 description="Weighting given to the adhesion direction",
690 default=0.1,
691 min=0.0,
692 soft_max=1.0
694 branchingProbability: FloatProperty(
695 name="Branching Probability",
696 description="Probability of a new branch forming",
697 default=0.05,
698 min=0.0,
699 soft_max=1.0
701 leafProbability: FloatProperty(
702 name="Leaf Probability",
703 description="Probability of a leaf forming",
704 default=0.35,
705 min=0.0,
706 soft_max=1.0
708 ivySize: FloatProperty(
709 name="Ivy Size",
710 description="The length of an ivy segment in Blender"
711 " Units",
712 default=0.02,
713 min=0.0,
714 soft_max=1.0,
715 precision=3
717 ivyLeafSize: FloatProperty(
718 name="Ivy Leaf Size",
719 description="The size of the ivy leaves",
720 default=0.02,
721 min=0.0,
722 soft_max=0.5,
723 precision=3
725 ivyBranchSize: FloatProperty(
726 name="Ivy Branch Size",
727 description="The size of the ivy branches",
728 default=0.001,
729 min=0.0,
730 soft_max=0.1,
731 precision=4
733 maxFloatLength: FloatProperty(
734 name="Max Float Length",
735 description="The maximum distance that a branch "
736 "can live while floating",
737 default=0.5,
738 min=0.0,
739 soft_max=1.0
741 maxAdhesionDistance: FloatProperty(
742 name="Max Adhesion Length",
743 description="The maximum distance that a branch "
744 "will feel the effects of adhesion",
745 default=1.0,
746 min=0.0,
747 soft_max=2.0,
748 precision=2
750 randomSeed: IntProperty(
751 name="Random Seed",
752 description="The seed governing random generation",
753 default=0,
754 min=0
756 maxTime: FloatProperty(
757 name="Maximum Time",
758 description="The maximum time to run the generation for "
759 "in seconds generation (0.0 = Disabled)",
760 default=0.0,
761 min=0.0,
762 soft_max=10
764 growLeaves: BoolProperty(
765 name="Grow Leaves",
766 description="Grow leaves or not",
767 default=True
771 classes = (
772 IvyGen,
773 IvyGenProperties,
774 CURVE_PT_IvyGenPanel
778 def register():
779 for cls in classes:
780 bpy.utils.register_class(cls)
782 bpy.types.WindowManager.ivy_gen_props = PointerProperty(
783 type=IvyGenProperties
787 def unregister():
788 del bpy.types.WindowManager.ivy_gen_props
790 for cls in reversed(classes):
791 bpy.utils.unregister_class(cls)
794 if __name__ == "__main__":
795 register()