diff --git a/object_cloud_gen.py b/object_cloud_gen.py index e2388428157f8a6df80e8a9b4c6a5fa33300bef5..9cd9cb7c62576607d4df6cfadf8876f2a7715e3c 100644 --- a/object_cloud_gen.py +++ b/object_cloud_gen.py @@ -1,4 +1,4 @@ - # ##### BEGIN GPL LICENSE BLOCK ##### +# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,7 +20,7 @@ bl_addon_info = { 'name': 'Object: Cloud Generator', 'author': 'Nick Keeline(nrk)', - 'version': '0.5', + 'version': '0.6', 'blender': (2, 5, 3), 'location': 'Tool Shelf ', 'description': 'Creates Volumetric Clouds', @@ -41,6 +41,7 @@ Rev 0.2 Added Point Density turbulence and fixed degenerate Rev 0.3 Fixed bug in degenerate Rev 0.4 updated for api change/changed to new apply modifier technique Rev 0.5 made particle count equation with radius so radius increases with cloud volume +Rev 0.6 added poll function to operator, fixing crash with no selected objects """ import bpy @@ -48,9 +49,6 @@ import mathutils from math import * from bpy.props import * -# Deselect All -bpy.ops.object.select_all(action='DESELECT') - # This routine takes an object and deletes all of the geometry in it # and adds a bounding box to it. @@ -281,23 +279,6 @@ class VIEW3D_PT_tools_cloud(View3DPanel): col.label(text="a cloud.") # col.label(active_obj["CloudMember"]) -classes = [VIEW3D_PT_tools_cloud] - - -def register(): - register = bpy.types.register - for cls in classes: - register(cls) - - -def unregister(): - unregister = bpy.types.unregister - for cls in classes: - unregister(cls) - -if __name__ == "__main__": - register() - class GenerateCloud(bpy.types.Operator): bl_idname = "cloud.generate_cloud" @@ -306,6 +287,12 @@ class GenerateCloud(bpy.types.Operator): bl_register = True bl_undo = True + def poll(self, context): + if not context.active_object: + return False + else: + return (context.active_object.type=='MESH') + def execute(self, context): # Make variable that is the current .blend file main data blocks main = context.main @@ -316,323 +303,331 @@ class GenerateCloud(bpy.types.Operator): # Make variable scene that is current scene scene = context.scene - if active_object and active_object.type == 'MESH': - # Parameters the user may want to change: - # Number of points this number is multiplied by the volume to get - # the number of points the scripts will put in the volume. - numOfPoints = 1.0 - maxNumOfPoints = 100000 - scattering = 2.5 - pointDensityRadiusFactor = 1.0 - densityScale = 1.5 - - # Should we degnerate? - degenerate = degenerateCloud(active_object) - - if degenerate: - if active_object is not None: - # Degenerate Cloud - mainObj = active_object - - cloudMembers = active_object.children - - createdObjects = [] - definitionObjects = [] - for member in cloudMembers: - applyScaleRotLoc(scene, member) - if (member["CloudMember"] == "CreatedObj"): - createdObjects.append(member) - else: - definitionObjects.append(member) - - for defObj in definitionObjects: - # @todo check if it wouldn't be better to remove this - # in the first place (see del() in degenerateCloud) - #totally agree didn't know how before now...thanks! done. - if "CloudMember" in defObj: - del(defObj["CloudMember"]) - - for createdObj in createdObjects: - # Deselect All - bpy.ops.object.select_all(action='DESELECT') + # Parameters the user may want to change: + # Number of points this number is multiplied by the volume to get + # the number of points the scripts will put in the volume. + numOfPoints = 1.0 + maxNumOfPoints = 100000 + scattering = 2.5 + pointDensityRadiusFactor = 1.0 + densityScale = 1.5 + + # Should we degnerate? + degenerate = degenerateCloud(active_object) + + if degenerate: + # Degenerate Cloud + mainObj = active_object + + cloudMembers = active_object.children + + createdObjects = [] + definitionObjects = [] + for member in cloudMembers: + applyScaleRotLoc(scene, member) + if (member["CloudMember"] == "CreatedObj"): + createdObjects.append(member) + else: + definitionObjects.append(member) + + for defObj in definitionObjects: + # @todo check if it wouldn't be better to remove this + # in the first place (see del() in degenerateCloud) + #totally agree didn't know how before now...thanks! done. + if "CloudMember" in defObj: + del(defObj["CloudMember"]) + + for createdObj in createdObjects: + # Deselect All + bpy.ops.object.select_all(action='DESELECT') - # Select the object and delete it. - createdObj.selected = True - scene.objects.active = createdObj - bpy.ops.object.delete() - - # Delete the main object - # Deselect All - bpy.ops.object.select_all(action='DESELECT') + # Select the object and delete it. + createdObj.selected = True + scene.objects.active = createdObj + bpy.ops.object.delete() + + # Delete the main object + # Deselect All + bpy.ops.object.select_all(action='DESELECT') - # Select the object and delete it. - mainObj.selected = True - scene.objects.active = mainObj + # Select the object and delete it. + mainObj.selected = True + scene.objects.active = mainObj - # Delete all material slots in mainObj object - for i in range(len(mainObj.material_slots)): - mainObj.active_material_index = i - 1 - bpy.ops.object.material_slot_remove() + # Delete all material slots in mainObj object + for i in range(len(mainObj.material_slots)): + mainObj.active_material_index = i - 1 + bpy.ops.object.material_slot_remove() - # Delete the Main Object - bpy.ops.object.delete() + # Delete the Main Object + bpy.ops.object.delete() - # Select all of the left over boxes so people can immediately - # press generate again if they want. - for eachMember in definitionObjects: - eachMember.max_draw_type = 'SOLID' - eachMember.selected = True - scene.objects.active = eachMember + # Select all of the left over boxes so people can immediately + # press generate again if they want. + for eachMember in definitionObjects: + eachMember.max_draw_type = 'SOLID' + eachMember.selected = True + scene.objects.active = eachMember - else: - # Generate Cloud - - ###############Create Combined Object bounds################## - # Make a list of all Selected objects. - selectedObjects = bpy.context.selected_objects - - # Create a new object bounds - if selectedObjects is None: - bounds = addNewObject(scene, - "CloudBounds", - []) - - else: - bounds = addNewObject(scene, - "CloudBounds", - selectedObjects[0]) - - bounds.max_draw_type = 'BOUNDS' - bounds.restrict_render = False - - # Just add a Definition Property designating this - # as the main object. - bounds["CloudMember"] = "MainObj" - - # Since we used iteration 0 to copy with object we - # delete it off the list. - firstObject = selectedObjects[0] - del selectedObjects[0] - - # Apply location Rotation and Scale to all objects involved. - applyScaleRotLoc(scene, bounds) - for each in selectedObjects: - applyScaleRotLoc(scene, each) - - # Let's combine all of them together. - combineObjects(scene, bounds, selectedObjects) - - # Let's add some property info to the objects. - for selObj in selectedObjects: - selObj["CloudMember"] = "DefinitioinObj" - selObj.name = "DefinitioinObj" - selObj.max_draw_type = 'WIRE' - selObj.restrict_render = True - makeParent(bounds, selObj, scene) - - # Do the same to the 1. object since it is no longer in list. - firstObject["CloudMember"] = "DefinitioinObj" - firstObject.name = "DefinitioinObj" - firstObject.max_draw_type = 'WIRE' - firstObject.restrict_render = True - makeParent(bounds, firstObject, scene) - - ###############Create Cloud for putting Cloud Mesh############ - # Create a new object cloud. - cloud = addNewObject(scene, "CloudMesh", bounds) - cloud["CloudMember"] = "CreatedObj" - cloud.max_draw_type = 'WIRE' - cloud.restrict_render = True - - makeParent(bounds, cloud, scene) - - bpy.ops.object.editmode_toggle() - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.subdivide(number_cuts=2, fractal=0, smoothness=1) - bpy.ops.object.location_apply() - bpy.ops.mesh.vertices_smooth(repeat=20) - bpy.ops.mesh.tris_convert_to_quads() - bpy.ops.mesh.faces_shade_smooth() - bpy.ops.object.editmode_toggle() - - ###############Create Particles in cloud obj################## - # Turn off gravity. - scene.use_gravity = False - - # Set time to 0. - scene.frame_current = 0 - - # Add a new particle system. - bpy.ops.object.particle_system_add() - - #Particle settings setting it up! - cloudParticles = cloud.active_particle_system - cloudParticles.name = "CloudParticles" - cloudParticles.settings.frame_start = 0 - cloudParticles.settings.frame_end = 0 - cloudParticles.settings.emit_from = 'VOLUME' - cloudParticles.settings.draw_as = 'DOT' - cloudParticles.settings.ren_as = 'NONE' - cloudParticles.settings.normal_factor = 0 - cloudParticles.settings.distribution = 'RAND' - - ####################Create Volume Material#################### - # Deselect All - bpy.ops.object.select_all(action='DESELECT') - - # Select the object. - bounds.selected = True - scene.objects.active = bounds - - # Turn bounds object into a box. - makeObjectIntoBoundBox(bounds, .2) - - # Delete all material slots in bounds object. - for i in range(len(bounds.material_slots)): - bounds.active_material_index = i - 1 - bpy.ops.object.material_slot_remove() - - # Add a new material. - cloudMaterial = main.materials.new("CloudMaterial") - bpy.ops.object.material_slot_add() - bounds.material_slots[0].material = cloudMaterial - - # Set Up the Cloud Material - cloudMaterial.name = "CloudMaterial" - cloudMaterial.type = 'VOLUME' - mVolume = cloudMaterial.volume - mVolume.scattering = scattering - mVolume.density = 0 - mVolume.density_scale = densityScale - mVolume.transmission_color = [3, 3, 3] - mVolume.step_size = 0.1 - mVolume.light_cache = True - mVolume.cache_resolution = 75 - - # Add a texture - vMaterialTextureSlots = cloudMaterial.texture_slots - cloudtex = main.textures.new("CloudTex") - cloudMaterial.add_texture(cloudtex, 'ORCO') - cloudtex.type = 'CLOUDS' - cloudtex.noise_type = 'HARD_NOISE' - cloudtex.noise_size = 2 - - # Add a texture - cloudPointDensity = main.textures.new("CloudPointDensity") - cloudPointDensity.type = 'POINT_DENSITY' - cloudMaterial.add_texture(cloudPointDensity, 'ORCO') - pDensity = vMaterialTextureSlots[1].texture - vMaterialTextureSlots[1].map_density = True - vMaterialTextureSlots[1].rgb_to_intensity = True - vMaterialTextureSlots[1].texture_coordinates = 'GLOBAL' - pDensity.pointdensity.vertices_cache = 'WORLD_SPACE' - pDensity.pointdensity.turbulence = True - pDensity.pointdensity.noise_basis = 'VORONOI_F2' - pDensity.pointdensity.turbulence_depth = 3 - - pDensity.use_color_ramp = True - pRamp = pDensity.color_ramp - pRamp.interpolation = 'LINEAR' - pRampElements = pRamp.elements - #pRampElements[1].position = .9 - #pRampElements[1].color = [.18,.18,.18,.8] - - # Estimate the number of particles for the size of bounds. - volumeBoundBox = (bounds.dimensions[0] * bounds.dimensions[1]* bounds.dimensions[2]) - numParticles = int((2.4462 * volumeBoundBox + 430.4) * numOfPoints) - if numParticles > maxNumOfPoints: - numParticles = maxNumOfPoints - print(numParticles) + else: + # Generate Cloud + + ###############Create Combined Object bounds################## + # Make a list of all Selected objects. + selectedObjects = bpy.context.selected_objects + if not selectedObjects: + selectedObjects = [bpy.context.active_object] + + # Create a new object bounds + bounds = addNewObject(scene, + "CloudBounds", + selectedObjects[0]) + + bounds.max_draw_type = 'BOUNDS' + bounds.restrict_render = False + + # Just add a Definition Property designating this + # as the main object. + bounds["CloudMember"] = "MainObj" + + # Since we used iteration 0 to copy with object we + # delete it off the list. + firstObject = selectedObjects[0] + del selectedObjects[0] + + # Apply location Rotation and Scale to all objects involved. + applyScaleRotLoc(scene, bounds) + for each in selectedObjects: + applyScaleRotLoc(scene, each) + + # Let's combine all of them together. + combineObjects(scene, bounds, selectedObjects) + + # Let's add some property info to the objects. + for selObj in selectedObjects: + selObj["CloudMember"] = "DefinitioinObj" + selObj.name = "DefinitioinObj" + selObj.max_draw_type = 'WIRE' + selObj.restrict_render = True + makeParent(bounds, selObj, scene) + + # Do the same to the 1. object since it is no longer in list. + firstObject["CloudMember"] = "DefinitioinObj" + firstObject.name = "DefinitioinObj" + firstObject.max_draw_type = 'WIRE' + firstObject.restrict_render = True + makeParent(bounds, firstObject, scene) + + ###############Create Cloud for putting Cloud Mesh############ + # Create a new object cloud. + cloud = addNewObject(scene, "CloudMesh", bounds) + cloud["CloudMember"] = "CreatedObj" + cloud.max_draw_type = 'WIRE' + cloud.restrict_render = True + + makeParent(bounds, cloud, scene) + + bpy.ops.object.editmode_toggle() + bpy.ops.mesh.select_all(action='SELECT') + bpy.ops.mesh.subdivide(number_cuts=2, fractal=0, smoothness=1) + bpy.ops.object.location_apply() + bpy.ops.mesh.vertices_smooth(repeat=20) + bpy.ops.mesh.tris_convert_to_quads() + bpy.ops.mesh.faces_shade_smooth() + bpy.ops.object.editmode_toggle() + + ###############Create Particles in cloud obj################## + # Turn off gravity. + scene.use_gravity = False + + # Set time to 0. + scene.frame_current = 0 + + # Add a new particle system. + bpy.ops.object.particle_system_add() + + #Particle settings setting it up! + cloudParticles = cloud.active_particle_system + cloudParticles.name = "CloudParticles" + cloudParticles.settings.frame_start = 0 + cloudParticles.settings.frame_end = 0 + cloudParticles.settings.emit_from = 'VOLUME' + cloudParticles.settings.draw_as = 'DOT' + cloudParticles.settings.ren_as = 'NONE' + cloudParticles.settings.normal_factor = 0 + cloudParticles.settings.distribution = 'RAND' + + ####################Create Volume Material#################### + # Deselect All + bpy.ops.object.select_all(action='DESELECT') + + # Select the object. + bounds.selected = True + scene.objects.active = bounds + + # Turn bounds object into a box. + makeObjectIntoBoundBox(bounds, .2) + + # Delete all material slots in bounds object. + for i in range(len(bounds.material_slots)): + bounds.active_material_index = i - 1 + bpy.ops.object.material_slot_remove() + + # Add a new material. + cloudMaterial = main.materials.new("CloudMaterial") + bpy.ops.object.material_slot_add() + bounds.material_slots[0].material = cloudMaterial + + # Set Up the Cloud Material + cloudMaterial.name = "CloudMaterial" + cloudMaterial.type = 'VOLUME' + mVolume = cloudMaterial.volume + mVolume.scattering = scattering + mVolume.density = 0 + mVolume.density_scale = densityScale + mVolume.transmission_color = [3, 3, 3] + mVolume.step_size = 0.1 + mVolume.light_cache = True + mVolume.cache_resolution = 75 + + # Add a texture + vMaterialTextureSlots = cloudMaterial.texture_slots + cloudtex = main.textures.new("CloudTex") + cloudMaterial.add_texture(cloudtex, 'ORCO') + cloudtex.type = 'CLOUDS' + cloudtex.noise_type = 'HARD_NOISE' + cloudtex.noise_size = 2 + + # Add a texture + cloudPointDensity = main.textures.new("CloudPointDensity") + cloudPointDensity.type = 'POINT_DENSITY' + cloudMaterial.add_texture(cloudPointDensity, 'ORCO') + pDensity = vMaterialTextureSlots[1].texture + vMaterialTextureSlots[1].map_density = True + vMaterialTextureSlots[1].rgb_to_intensity = True + vMaterialTextureSlots[1].texture_coordinates = 'GLOBAL' + pDensity.pointdensity.vertices_cache = 'WORLD_SPACE' + pDensity.pointdensity.turbulence = True + pDensity.pointdensity.noise_basis = 'VORONOI_F2' + pDensity.pointdensity.turbulence_depth = 3 + + pDensity.use_color_ramp = True + pRamp = pDensity.color_ramp + pRamp.interpolation = 'LINEAR' + pRampElements = pRamp.elements + #pRampElements[1].position = .9 + #pRampElements[1].color = [.18,.18,.18,.8] + + # Estimate the number of particles for the size of bounds. + volumeBoundBox = (bounds.dimensions[0] * bounds.dimensions[1]* bounds.dimensions[2]) + numParticles = int((2.4462 * volumeBoundBox + 430.4) * numOfPoints) + if numParticles > maxNumOfPoints: + numParticles = maxNumOfPoints + print(numParticles) - # Set the number of particles according to the volume - # of bounds. - cloudParticles.settings.amount = numParticles + # Set the number of particles according to the volume + # of bounds. + cloudParticles.settings.amount = numParticles - pDensity.pointdensity.radius = (.00013764 * volumeBoundBox + .3989) * pointDensityRadiusFactor + pDensity.pointdensity.radius = (.00013764 * volumeBoundBox + .3989) * pointDensityRadiusFactor - # Set time to 1. - scene.frame_current = 1 + # Set time to 1. + scene.frame_current = 1 - ###############Create CloudPnts for putting points in######### - # Create a new object cloudPnts - cloudPnts = addNewObject(scene, "CloudPoints", bounds) - cloudPnts["CloudMember"] = "CreatedObj" - cloudPnts.max_draw_type = 'WIRE' - cloudPnts.restrict_render = True + ###############Create CloudPnts for putting points in######### + # Create a new object cloudPnts + cloudPnts = addNewObject(scene, "CloudPoints", bounds) + cloudPnts["CloudMember"] = "CreatedObj" + cloudPnts.max_draw_type = 'WIRE' + cloudPnts.restrict_render = True - makeParent(bounds, cloudPnts, scene) + makeParent(bounds, cloudPnts, scene) - bpy.ops.object.editmode_toggle() - bpy.ops.mesh.select_all(action='SELECT') + bpy.ops.object.editmode_toggle() + bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.delete(type='ALL') + bpy.ops.mesh.delete(type='ALL') - meshPnts = cloudPnts.data + meshPnts = cloudPnts.data - listCloudParticles = cloudParticles.particles + listCloudParticles = cloudParticles.particles - listMeshPnts = [] - for pTicle in listCloudParticles: - listMeshPnts.append(pTicle.location) + listMeshPnts = [] + for pTicle in listCloudParticles: + listMeshPnts.append(pTicle.location) - # Must be in object mode fro from_pydata to work. - bpy.ops.object.mode_set(mode='OBJECT') + # Must be in object mode fro from_pydata to work. + bpy.ops.object.mode_set(mode='OBJECT') - # Add in the mesh data. - meshPnts.from_pydata(listMeshPnts, [], []) + # Add in the mesh data. + meshPnts.from_pydata(listMeshPnts, [], []) - # Update the mesh. - meshPnts.update() + # Update the mesh. + meshPnts.update() - # Add a modifier. - bpy.ops.object.modifier_add(type='DISPLACE') + # Add a modifier. + bpy.ops.object.modifier_add(type='DISPLACE') - cldPntsModifiers = cloudPnts.modifiers - cldPntsModifiers[0].name = "CloudPnts" - cldPntsModifiers[0].texture = cloudtex - cldPntsModifiers[0].texture_coordinates = 'OBJECT' - cldPntsModifiers[0].texture_coordinate_object = cloud - cldPntsModifiers[0].strength = -1.4 + cldPntsModifiers = cloudPnts.modifiers + cldPntsModifiers[0].name = "CloudPnts" + cldPntsModifiers[0].texture = cloudtex + cldPntsModifiers[0].texture_coordinates = 'OBJECT' + cldPntsModifiers[0].texture_coordinate_object = cloud + cldPntsModifiers[0].strength = -1.4 - # Apply modifier - bpy.ops.object.modifier_apply(apply_as='DATA', modifier=cldPntsModifiers[0].name) + # Apply modifier + bpy.ops.object.modifier_apply(apply_as='DATA', modifier=cldPntsModifiers[0].name) - pDensity.pointdensity.point_source = 'OBJECT' - pDensity.pointdensity.object = cloudPnts + pDensity.pointdensity.point_source = 'OBJECT' + pDensity.pointdensity.object = cloudPnts - # Deselect All - bpy.ops.object.select_all(action='DESELECT') + # Deselect All + bpy.ops.object.select_all(action='DESELECT') - # Select the object. - cloud.selected = True - scene.objects.active = cloud + # Select the object. + cloud.selected = True + scene.objects.active = cloud - bpy.ops.object.particle_system_remove() + bpy.ops.object.particle_system_remove() - # Deselect All - bpy.ops.object.select_all(action='DESELECT') + # Deselect All + bpy.ops.object.select_all(action='DESELECT') - # Select the object. - bounds.selected = True - scene.objects.active = bounds + # Select the object. + bounds.selected = True + scene.objects.active = bounds - # Add a force field to the points. - #cloudField = bounds.field - #cloudField.type = 'TEXTURE' - #cloudField.strength = 2 - #cloudField.texture = cloudtex + # Add a force field to the points. + #cloudField = bounds.field + #cloudField.type = 'TEXTURE' + #cloudField.strength = 2 + #cloudField.texture = cloudtex - # Set time - #for i in range(12): - # scene.current_frame = i - # scene.update() - #bpy.ops.ptcache.bake_all(bake=False) + # Set time + #for i in range(12): + # scene.current_frame = i + # scene.update() + #bpy.ops.ptcache.bake_all(bake=False) - #self.report({'WARNING'}, "Generating Cloud") return {'FINISHED'} -bpy.types.register(GenerateCloud) + +classes = [VIEW3D_PT_tools_cloud, + GenerateCloud] + + +def register(): + register = bpy.types.register + for cls in classes: + register(cls) + + +def unregister(): + unregister = bpy.types.unregister + for cls in classes: + unregister(cls) + if __name__ == "__main__": - bpy.ops.cloud.generate_cloud() \ No newline at end of file + register() \ No newline at end of file