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 #####
22 "name": "Cloud Generator",
23 "author": "Nick Keeline(nrk)",
25 "blender": (2, 59, 0),
26 "location": "View3D > Tool Shelf > Cloud Generator Panel",
27 "description": "Creates Volumetric Clouds",
28 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
29 "Scripts/Object/Cloud_Gen",
30 "tracker_url": "https://developer.blender.org/T22015",
34 from bpy
.props
import BoolProperty
, EnumProperty
35 from bpy
.types
import Operator
, Panel
38 # This routine takes an object and deletes all of the geometry in it
39 # and adds a bounding box to it.
40 # It will add or subtract the bound box size by the variable sizeDifference.
42 def getMeshandPutinEditMode(scene
, object):
45 bpy
.ops
.object.mode_set(mode
='OBJECT')
48 bpy
.ops
.object.select_all(action
='DESELECT')
52 scene
.objects
.active
= object
55 bpy
.ops
.object.mode_set(mode
='EDIT')
60 def maxAndMinVerts(scene
, object):
62 mesh
= getMeshandPutinEditMode(scene
, object)
65 #Set the max and min verts to the first vertex on the list
66 maxVert
= [verts
[0].co
[0], verts
[0].co
[1], verts
[0].co
[2]]
67 minVert
= [verts
[0].co
[0], verts
[0].co
[1], verts
[0].co
[2]]
69 #Create Max and Min Vertex array for the outer corners of the box
72 if vert
.co
[0] > maxVert
[0]:
73 maxVert
[0] = vert
.co
[0]
74 if vert
.co
[1] > maxVert
[1]:
75 maxVert
[1] = vert
.co
[1]
76 if vert
.co
[2] > maxVert
[2]:
77 maxVert
[2] = vert
.co
[2]
80 if vert
.co
[0] < minVert
[0]:
81 minVert
[0] = vert
.co
[0]
82 if vert
.co
[1] < minVert
[1]:
83 minVert
[1] = vert
.co
[1]
84 if vert
.co
[2] < minVert
[2]:
85 minVert
[2] = vert
.co
[2]
87 return [maxVert
, minVert
]
90 def makeObjectIntoBoundBox(scene
, object, sizeDifference
, takeFromObject
):
92 # Let's find the max and min of the reference object,
93 # it can be the same as the destination object
94 [maxVert
, minVert
] = maxAndMinVerts(scene
, takeFromObject
)
97 mesh
= getMeshandPutinEditMode(scene
, object)
99 #Add the size difference to the max size of the box
100 maxVert
[0] = maxVert
[0] + sizeDifference
101 maxVert
[1] = maxVert
[1] + sizeDifference
102 maxVert
[2] = maxVert
[2] + sizeDifference
104 #subtract the size difference to the min size of the box
105 minVert
[0] = minVert
[0] - sizeDifference
106 minVert
[1] = minVert
[1] - sizeDifference
107 minVert
[2] = minVert
[2] - sizeDifference
109 #Create arrays of verts and faces to be added to the mesh
113 addVerts
.append([maxVert
[0], maxVert
[1], maxVert
[2]])
114 addVerts
.append([maxVert
[0], maxVert
[1], minVert
[2]])
115 addVerts
.append([maxVert
[0], minVert
[1], minVert
[2]])
116 addVerts
.append([maxVert
[0], minVert
[1], maxVert
[2]])
119 addVerts
.append([minVert
[0], maxVert
[1], maxVert
[2]])
120 addVerts
.append([minVert
[0], maxVert
[1], minVert
[2]])
121 addVerts
.append([minVert
[0], minVert
[1], minVert
[2]])
122 addVerts
.append([minVert
[0], minVert
[1], maxVert
[2]])
124 # Make the faces of the bounding box.
127 # Draw a box on paper and number the vertices.
128 # Use right hand rule to come up with number orders for faces on
129 # the box (with normals pointing out).
130 addFaces
.append([0, 3, 2, 1])
131 addFaces
.append([4, 5, 6, 7])
132 addFaces
.append([0, 1, 5, 4])
133 addFaces
.append([1, 2, 6, 5])
134 addFaces
.append([2, 3, 7, 6])
135 addFaces
.append([0, 4, 7, 3])
137 # Delete all geometry from the object.
138 bpy
.ops
.mesh
.select_all(action
='SELECT')
139 bpy
.ops
.mesh
.delete(type='VERT')
141 # Must be in object mode for from_pydata to work
142 bpy
.ops
.object.mode_set(mode
='OBJECT')
145 mesh
.from_pydata(addVerts
, [], addFaces
)
151 def applyScaleRotLoc(scene
, obj
):
153 bpy
.ops
.object.select_all(action
='DESELECT')
157 scene
.objects
.active
= obj
159 bpy
.ops
.object.transform_apply(location
=True, rotation
=True, scale
=True)
162 def totallyDeleteObject(scene
, obj
):
163 scene
.objects
.unlink(obj
)
164 bpy
.data
.objects
.remove(obj
)
167 def makeParent(parentobj
, childobj
, scene
):
169 applyScaleRotLoc(scene
, parentobj
)
170 applyScaleRotLoc(scene
, childobj
)
171 childobj
.parent
= parentobj
174 def addNewObject(scene
, name
, copyobj
):
177 mesh
= bpy
.data
.meshes
.new(name
)
179 # Create a new object.
180 ob_new
= bpy
.data
.objects
.new(name
, mesh
)
181 tempme
= copyobj
.data
182 ob_new
.data
= tempme
.copy()
183 ob_new
.scale
= copyobj
.scale
184 ob_new
.location
= copyobj
.location
186 # Link new object to the given scene and select it.
187 scene
.objects
.link(ob_new
)
193 def getpdensitytexture(object):
195 for mslot
in object.material_slots
:
197 for tslot
in mat
.texture_slots
:
198 if tslot
!= 'NoneType':
200 if tex
.type == 'POINT_DENSITY':
201 if tex
.point_density
.point_source
== 'PARTICLE_SYSTEM':
205 def removeParticleSystemFromObj(scene
, object):
208 bpy
.ops
.object.select_all(action
='DESELECT')
212 scene
.objects
.active
= object
214 bpy
.ops
.object.particle_system_remove()
217 bpy
.ops
.object.select_all(action
='DESELECT')
220 def convertParticlesToMesh(scene
, particlesobj
, destobj
, replacemesh
):
222 # Select the Destination object.
223 destobj
.select
= True
224 scene
.objects
.active
= destobj
227 bpy
.ops
.object.mode_set(mode
='EDIT', toggle
=False)
229 #Delete everything in mesh if replace true
231 bpy
.ops
.mesh
.select_all(action
='SELECT')
232 bpy
.ops
.mesh
.delete(type='VERT')
234 meshPnts
= destobj
.data
236 listCloudParticles
= particlesobj
.particles
239 for pTicle
in listCloudParticles
:
240 listMeshPnts
.append(pTicle
.location
)
242 # Must be in object mode for from_pydata to work.
243 bpy
.ops
.object.mode_set(mode
='OBJECT')
245 # Add in the mesh data.
246 meshPnts
.from_pydata(listMeshPnts
, [], [])
252 def combineObjects(scene
, combined
, listobjs
):
253 # scene is the current scene
254 # combined is the object we want to combine everything into
255 # listobjs is the list of objects to stick into combined
258 bpy
.ops
.object.select_all(action
='DESELECT')
260 # Select the new object.
261 combined
.select
= True
262 scene
.objects
.active
= combined
265 if len(listobjs
) > 0:
268 bpy
.ops
.object.modifier_add(type='BOOLEAN')
270 union
= combined
.modifiers
271 union
[0].name
= "AddEmUp"
273 union
[0].operation
= 'UNION'
276 bpy
.ops
.object.modifier_apply(apply_as
='DATA', modifier
=union
[0].name
)
279 # Returns the action we want to take
280 def getActionToDo(obj
):
282 if not obj
or obj
.type != 'MESH':
283 return 'NOT_OBJ_DO_NOTHING'
285 return 'NO_SELECTION_DO_NOTHING'
286 elif "CloudMember" in obj
:
287 if obj
["CloudMember"] != None:
288 if obj
["CloudMember"] == "MainObj":
290 elif obj
["CloudMember"] == "CreatedObj" and len(obj
.particle_systems
) > 0:
291 return 'CLOUD_CONVERT_TO_MESH'
293 return 'CLOUD_DO_NOTHING'
294 elif obj
.type == 'MESH':
300 class VIEW3D_PT_tools_cloud(Panel
):
301 bl_space_type
= 'VIEW_3D'
302 bl_region_type
= 'TOOLS'
303 bl_category
= 'Create'
304 bl_label
= "Cloud Generator"
305 bl_context
= "objectmode"
306 bl_options
= {'DEFAULT_CLOSED'}
308 def draw(self
, context
):
309 active_obj
= context
.active_object
311 col
= layout
.column(align
=True)
313 WhatToDo
= getActionToDo(active_obj
)
315 if WhatToDo
== 'DEGENERATE':
316 col
.operator("cloud.generate_cloud", text
="DeGenerate")
318 elif WhatToDo
== 'CLOUD_CONVERT_TO_MESH':
319 col
.operator("cloud.generate_cloud", text
="Convert to Mesh")
321 elif WhatToDo
== 'NO_SELECTION_DO_NOTHING':
322 col
.label(text
="Select one or more")
323 col
.label(text
="objects to generate")
324 col
.label(text
="a cloud")
326 elif WhatToDo
== 'CLOUD_DO_NOTHING':
327 col
.label(text
="Must select")
328 col
.label(text
="bound box")
330 elif WhatToDo
== 'GENERATE':
331 col
.operator("cloud.generate_cloud", text
="Generate Cloud")
333 col
.prop(context
.scene
, "cloud_type")
334 col
.prop(context
.scene
, "cloudparticles")
335 col
.prop(context
.scene
, "cloudsmoothing")
337 col
.label(text
="Select one or more")
338 col
.label(text
="objects to generate")
339 col
.label(text
="a cloud")
342 class GenerateCloud(Operator
):
343 """Create a Cloud,Undo Cloud, or convert to Mesh Cloud depending on selection"""
344 bl_idname
= "cloud.generate_cloud"
345 bl_label
= "Generate Cloud"
350 def poll(cls
, context
):
351 if not context
.active_object
:
354 return (context
.active_object
.type == 'MESH')
356 def execute(self
, context
):
357 # Make variable that is the current .blend file main data blocks
358 blend_data
= context
.blend_data
360 # Make variable that is the active object selected by user
361 active_object
= context
.active_object
363 # Make variable scene that is current scene
364 scene
= context
.scene
366 # Parameters the user may want to change:
367 # Number of points this number is multiplied by the volume to get
368 # the number of points the scripts will put in the volume.
370 maxNumOfPoints
= 100000
371 maxPointDensityRadius
= 1.5
373 pointDensityRadiusFactor
= 1.0
377 WhatToDo
= getActionToDo(active_object
)
379 if WhatToDo
== 'DEGENERATE':
381 mainObj
= active_object
383 cloudMembers
= active_object
.children
386 definitionObjects
= []
387 for member
in cloudMembers
:
388 applyScaleRotLoc(scene
, member
)
389 if member
["CloudMember"] == "CreatedObj":
390 createdObjects
.append(member
)
392 definitionObjects
.append(member
)
394 for defObj
in definitionObjects
:
395 # Delete cloudmember data from objects
396 if "CloudMember" in defObj
:
397 del(defObj
["CloudMember"])
399 for createdObj
in createdObjects
:
400 totallyDeleteObject(scene
, createdObj
)
402 # Delete the blend_data object
403 totallyDeleteObject(scene
, mainObj
)
405 # Select all of the left over boxes so people can immediately
406 # press generate again if they want.
407 for eachMember
in definitionObjects
:
408 eachMember
.draw_type
= 'SOLID'
409 eachMember
.select
= True
410 eachMember
.hide_render
= False
412 elif WhatToDo
== 'CLOUD_CONVERT_TO_MESH':
414 cloudParticles
= active_object
.particle_systems
.active
416 bounds
= active_object
.parent
418 ###############Create CloudPnts for putting points in#########
419 # Create a new object cloudPnts
420 cloudPnts
= addNewObject(scene
, "CloudPoints", bounds
)
421 cloudPnts
["CloudMember"] = "CreatedObj"
422 cloudPnts
.draw_type
= 'WIRE'
423 cloudPnts
.hide_render
= True
425 makeParent(bounds
, cloudPnts
, scene
)
427 convertParticlesToMesh(scene
, cloudParticles
, cloudPnts
, True)
429 removeParticleSystemFromObj(scene
, active_object
)
431 pDensity
= getpdensitytexture(bounds
)
432 pDensity
.point_density
.point_source
= 'OBJECT'
433 pDensity
.point_density
.object = cloudPnts
435 #Let's resize the bound box to be more accurate.
436 how_much_bigger
= pDensity
.point_density
.radius
437 makeObjectIntoBoundBox(scene
, bounds
, how_much_bigger
, cloudPnts
)
442 ###############Create Combined Object bounds##################
443 # Make a list of all Selected objects.
444 selectedObjects
= bpy
.context
.selected_objects
445 if not selectedObjects
:
446 selectedObjects
= [bpy
.context
.active_object
]
448 # Create a new object bounds
449 bounds
= addNewObject(scene
,
453 bounds
.draw_type
= 'BOUNDS'
454 bounds
.hide_render
= False
456 # Just add a Definition Property designating this
457 # as the blend_data object.
458 bounds
["CloudMember"] = "MainObj"
460 # Since we used iteration 0 to copy with object we
461 # delete it off the list.
462 firstObject
= selectedObjects
[0]
463 del selectedObjects
[0]
465 # Apply location Rotation and Scale to all objects involved.
466 applyScaleRotLoc(scene
, bounds
)
467 for each
in selectedObjects
:
468 applyScaleRotLoc(scene
, each
)
470 # Let's combine all of them together.
471 combineObjects(scene
, bounds
, selectedObjects
)
473 # Let's add some property info to the objects.
474 for selObj
in selectedObjects
:
475 selObj
["CloudMember"] = "DefinitioinObj"
476 selObj
.name
= "DefinitioinObj"
477 selObj
.draw_type
= 'WIRE'
478 selObj
.hide_render
= True
479 makeParent(bounds
, selObj
, scene
)
481 # Do the same to the 1. object since it is no longer in list.
482 firstObject
["CloudMember"] = "DefinitioinObj"
483 firstObject
.name
= "DefinitioinObj"
484 firstObject
.draw_type
= 'WIRE'
485 firstObject
.hide_render
= True
486 makeParent(bounds
, firstObject
, scene
)
488 ###############Create Cloud for putting Cloud Mesh############
489 # Create a new object cloud.
490 cloud
= addNewObject(scene
, "CloudMesh", bounds
)
491 cloud
["CloudMember"] = "CreatedObj"
492 cloud
.draw_type
= 'WIRE'
493 cloud
.hide_render
= True
495 makeParent(bounds
, cloud
, scene
)
497 bpy
.ops
.object.editmode_toggle()
498 bpy
.ops
.mesh
.select_all(action
='SELECT')
500 #Don't subdivide object or smooth if smoothing box not checked.
501 if scene
.cloudsmoothing
:
502 bpy
.ops
.mesh
.subdivide(number_cuts
=2, fractal
=0, smoothness
=1)
503 # bpy.ops.object.transform_apply(location=True)
504 bpy
.ops
.mesh
.vertices_smooth(repeat
=20)
505 bpy
.ops
.mesh
.tris_convert_to_quads()
506 bpy
.ops
.mesh
.faces_shade_smooth()
507 bpy
.ops
.object.editmode_toggle()
509 ###############Create Particles in cloud obj##################
512 scene
.frame_current
= 0
514 # Add a new particle system.
515 bpy
.ops
.object.particle_system_add()
517 #Particle settings setting it up!
518 cloudParticles
= cloud
.particle_systems
.active
519 cloudParticles
.name
= "CloudParticles"
520 cloudParticles
.settings
.frame_start
= 0
521 cloudParticles
.settings
.frame_end
= 0
522 cloudParticles
.settings
.emit_from
= 'VOLUME'
523 cloudParticles
.settings
.lifetime
= scene
.frame_end
524 cloudParticles
.settings
.draw_method
= 'DOT'
525 cloudParticles
.settings
.render_type
= 'NONE'
526 cloudParticles
.settings
.distribution
= 'RAND'
527 cloudParticles
.settings
.physics_type
= 'NEWTON'
528 cloudParticles
.settings
.normal_factor
= 0
530 #Gravity does not effect the particle system
531 eWeights
= cloudParticles
.settings
.effector_weights
534 ####################Create Volume Material####################
536 bpy
.ops
.object.select_all(action
='DESELECT')
540 scene
.objects
.active
= bounds
542 # Turn bounds object into a box. Use itself as a reference.
543 makeObjectIntoBoundBox(scene
, bounds
, 1.0, bounds
)
545 # Delete all material slots in bounds object.
546 for i
in range(len(bounds
.material_slots
)):
547 bounds
.active_material_index
= i
- 1
548 bpy
.ops
.object.material_slot_remove()
550 # Add a new material.
551 cloudMaterial
= blend_data
.materials
.new("CloudMaterial")
552 bpy
.ops
.object.material_slot_add()
553 bounds
.material_slots
[0].material
= cloudMaterial
555 # Set Up the Cloud Material
556 cloudMaterial
.name
= "CloudMaterial"
557 cloudMaterial
.type = 'VOLUME'
558 mVolume
= cloudMaterial
.volume
559 mVolume
.scattering
= scattering
561 mVolume
.density_scale
= densityScale
562 mVolume
.transmission_color
= 3.0, 3.0, 3.0
563 mVolume
.step_size
= 0.1
564 mVolume
.use_light_cache
= True
565 mVolume
.cache_resolution
= 45
568 # vMaterialTextureSlots = cloudMaterial.texture_slots # UNUSED
569 cloudtex
= blend_data
.textures
.new("CloudTex", type='CLOUDS')
570 cloudtex
.noise_type
= 'HARD_NOISE'
571 cloudtex
.noise_scale
= 2
572 mtex
= cloudMaterial
.texture_slots
.add()
573 mtex
.texture
= cloudtex
574 mtex
.texture_coords
= 'ORCO'
575 mtex
.use_map_color_diffuse
= True
578 scene
.frame_current
= 1
580 # Add a Point Density texture
581 pDensity
= blend_data
.textures
.new("CloudPointDensity", 'POINT_DENSITY')
583 mtex
= cloudMaterial
.texture_slots
.add()
584 mtex
.texture
= pDensity
585 mtex
.texture_coords
= 'GLOBAL'
586 mtex
.use_map_density
= True
587 mtex
.use_rgb_to_intensity
= True
588 mtex
.texture_coords
= 'GLOBAL'
590 pDensity
.point_density
.vertex_cache_space
= 'WORLD_SPACE'
591 pDensity
.point_density
.use_turbulence
= True
592 pDensity
.point_density
.noise_basis
= 'VORONOI_F2'
593 pDensity
.point_density
.turbulence_depth
= 3
595 pDensity
.use_color_ramp
= True
596 pRamp
= pDensity
.color_ramp
597 #pRamp.use_interpolation = 'LINEAR'
598 pRampElements
= pRamp
.elements
599 #pRampElements[1].position = .9
600 #pRampElements[1].color = 0.18, 0.18, 0.18, 0.8
601 bpy
.ops
.texture
.slot_move(type='UP')
603 # Estimate the number of particles for the size of bounds.
604 volumeBoundBox
= (bounds
.dimensions
[0] * bounds
.dimensions
[1] * bounds
.dimensions
[2])
605 numParticles
= int((2.4462 * volumeBoundBox
+ 430.4) * numOfPoints
)
606 if numParticles
> maxNumOfPoints
:
607 numParticles
= maxNumOfPoints
608 if numParticles
< 10000:
609 numParticles
= int(numParticles
+ 15 * volumeBoundBox
)
612 # Set the number of particles according to the volume
614 cloudParticles
.settings
.count
= numParticles
616 pDensity
.point_density
.radius
= (.00013764 * volumeBoundBox
+ .3989) * pointDensityRadiusFactor
618 if pDensity
.point_density
.radius
> maxPointDensityRadius
:
619 pDensity
.point_density
.radius
= maxPointDensityRadius
622 scene
.frame_current
= 1
624 if not scene
.cloudparticles
:
625 ###############Create CloudPnts for putting points in#########
626 # Create a new object cloudPnts
627 cloudPnts
= addNewObject(scene
, "CloudPoints", bounds
)
628 cloudPnts
["CloudMember"] = "CreatedObj"
629 cloudPnts
.draw_type
= 'WIRE'
630 cloudPnts
.hide_render
= True
632 makeParent(bounds
, cloudPnts
, scene
)
634 convertParticlesToMesh(scene
, cloudParticles
, cloudPnts
, True)
637 bpy
.ops
.object.modifier_add(type='DISPLACE')
639 cldPntsModifiers
= cloudPnts
.modifiers
640 cldPntsModifiers
[0].name
= "CloudPnts"
641 cldPntsModifiers
[0].texture
= cloudtex
642 cldPntsModifiers
[0].texture_coords
= 'OBJECT'
643 cldPntsModifiers
[0].texture_coords_object
= cloud
644 cldPntsModifiers
[0].strength
= -1.4
647 bpy
.ops
.object.modifier_apply(apply_as
='DATA', modifier
=cldPntsModifiers
[0].name
)
649 pDensity
.point_density
.point_source
= 'OBJECT'
650 pDensity
.point_density
.object = cloudPnts
652 removeParticleSystemFromObj(scene
, cloud
)
656 pDensity
.point_density
.point_source
= 'PARTICLE_SYSTEM'
657 pDensity
.point_density
.object = cloud
658 pDensity
.point_density
.particle_system
= cloudParticles
660 if scene
.cloud_type
== '1': # Cumulous
662 mVolume
.density_scale
= 2.22
663 pDensity
.point_density
.turbulence_depth
= 10
664 pDensity
.point_density
.turbulence_strength
= 6.3
665 pDensity
.point_density
.turbulence_scale
= 2.9
666 pRampElements
[1].position
= .606
667 pDensity
.point_density
.radius
= pDensity
.point_density
.radius
+ 0.1
669 elif scene
.cloud_type
== '2': # Cirrus
671 pDensity
.point_density
.turbulence_strength
= 22
672 mVolume
.transmission_color
= 3.5, 3.5, 3.5
673 mVolume
.scattering
= 0.13
675 elif scene
.cloud_type
== '3': # Explosion
676 mVolume
.emission
= 1.42
677 mtex
.use_rgb_to_intensity
= False
678 pRampElements
[0].position
= 0.825
679 pRampElements
[0].color
= 0.119, 0.119, 0.119, 1
680 pRampElements
[1].position
= .049
681 pRampElements
[1].color
= 1.0, 1.0, 1.0, 0
682 pDensity
.point_density
.turbulence_strength
= 1.5
683 pRampElement1
= pRampElements
.new(.452)
684 pRampElement1
.color
= 0.814, 0.112, 0, 1
685 pRampElement2
= pRampElements
.new(.234)
686 pRampElement2
.color
= 0.814, 0.310, 0.002, 1
687 pRampElement3
= pRampElements
.new(0.669)
688 pRampElement3
.color
= 0.0, 0.0, 0.040, 1
692 scene
.objects
.active
= bounds
694 #Let's resize the bound box to be more accurate.
695 how_much_bigger
= pDensity
.point_density
.radius
+ 0.1
697 #If it's a particle cloud use cloud mesh if otherwise use point mesh
698 if not scene
.cloudparticles
:
699 makeObjectIntoBoundBox(scene
, bounds
, how_much_bigger
, cloudPnts
)
701 makeObjectIntoBoundBox(scene
, bounds
, how_much_bigger
, cloud
)
707 bpy
.utils
.register_module(__name__
)
709 bpy
.types
.Scene
.cloudparticles
= BoolProperty(
711 description
="Generate Cloud as Particle System",
714 bpy
.types
.Scene
.cloudsmoothing
= BoolProperty(
716 description
="Smooth Resultant Geometry From Gen Cloud Operation",
719 bpy
.types
.Scene
.cloud_type
= EnumProperty(
721 description
="Select the type of cloud to create with material settings",
722 items
=[("0", "Stratus", "Generate Stratus_foggy Cloud"),
723 ("1", "Cumulous", "Generate Cumulous_puffy Cloud"),
724 ("2", "Cirrus", "Generate Cirrus_wispy Cloud"),
725 ("3", "Explosion", "Generate Explosion"),
731 bpy
.utils
.unregister_module(__name__
)
733 del bpy
.types
.Scene
.cloudparticles
734 del bpy
.types
.Scene
.cloud_type
737 if __name__
== "__main__":