diff --git a/object_cloud_gen.py b/object_cloud_gen.py deleted file mode 100644 index 5ddce14ca92e9c21a92363540440791ff82f3d96..0000000000000000000000000000000000000000 --- a/object_cloud_gen.py +++ /dev/null @@ -1,991 +0,0 @@ -# ##### 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 -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -bl_info = { - "name": "Cloud Generator", - "author": "Nick Keeline(nrk)", - "version": (1, 0, 2), - "blender": (2, 79, 0), - "location": "Tool Shelf > Create Tab", - "description": "Creates Volumetric Clouds", - "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" - "Scripts/Object/Cloud_Gen", - "category": "Object", -} - -import bpy -from bpy.props import ( - BoolProperty, - EnumProperty, - ) -from bpy.types import ( - Operator, - Panel, - ) - - -# For Cycles Render we create node groups or if it already exists we return it. -def CreateNodeGroup(Type): - - # Look for NodeTree if it already exists return it - CreateGroup = True - for Group in bpy.data.node_groups: - if Group.name == Type: - CreateGroup = False - NodeGroup = Group - - if CreateGroup is True: - NodeGroup = bpy.data.node_groups.new(name=Type, type="ShaderNodeTree") - NodeGroup.name = Type - NodeGroup.bl_label = Type - NodeGroup.nodes.clear() - - # Create a bunch of nodes and group them based on input to the def - # Function type - if Type == 'CloudGen_VolumeProperties': - AddAddAndEmission = NodeGroup.nodes.new('ShaderNodeAddShader') - AddAddAndEmission.location = [300, 395] - AddAbsorptionAndScatter = NodeGroup.nodes.new('ShaderNodeAddShader') - AddAbsorptionAndScatter.location = [0, 395] - VolumeAbsorption = NodeGroup.nodes.new('ShaderNodeVolumeAbsorption') - VolumeAbsorption.location = [-300, 395] - VolumeScatter = NodeGroup.nodes.new('ShaderNodeVolumeScatter') - VolumeScatter.location = [-300, 0] - VolumeEmission = NodeGroup.nodes.new('ShaderNodeEmission') - VolumeEmission.location = [-300, -300] - MathAbsorptionMultiply = NodeGroup.nodes.new('ShaderNodeMath') - MathAbsorptionMultiply.location = [-750, 395] - MathAbsorptionMultiply.operation = 'MULTIPLY' - MathScatterMultiply = NodeGroup.nodes.new('ShaderNodeMath') - MathScatterMultiply.location = [-750, 0] - MathScatterMultiply.operation = 'MULTIPLY' - MathEmissionMultiply = NodeGroup.nodes.new('ShaderNodeMath') - MathEmissionMultiply.location = [-750, -300] - MathEmissionMultiply.operation = 'MULTIPLY' - MathBrightnessMultiply = NodeGroup.nodes.new('ShaderNodeMath') - MathBrightnessMultiply.location = [-1200, 0] - MathBrightnessMultiply.operation = 'MULTIPLY' - MathGreaterThan = NodeGroup.nodes.new('ShaderNodeMath') - MathGreaterThan.location = [-1200, 600] - MathGreaterThan.operation = 'GREATER_THAN' - MathGreaterThan.inputs[1].default_value = 0 - - NodeGroup.links.new(AddAddAndEmission.inputs[0], AddAbsorptionAndScatter.outputs[0]) - NodeGroup.links.new(AddAddAndEmission.inputs[1], VolumeEmission.outputs[0]) - NodeGroup.links.new(AddAbsorptionAndScatter.inputs[0], VolumeAbsorption.outputs[0]) - NodeGroup.links.new(AddAbsorptionAndScatter.inputs[1], VolumeScatter.outputs[0]) - NodeGroup.links.new(VolumeAbsorption.inputs[1], MathAbsorptionMultiply.outputs[0]) - NodeGroup.links.new(VolumeScatter.inputs[1], MathScatterMultiply.outputs[0]) - NodeGroup.links.new(VolumeEmission.inputs[1], MathEmissionMultiply.outputs[0]) - NodeGroup.links.new(MathAbsorptionMultiply.inputs[0], MathGreaterThan.outputs[0]) - NodeGroup.links.new(MathScatterMultiply.inputs[0], MathGreaterThan.outputs[0]) - NodeGroup.links.new(MathEmissionMultiply.inputs[0], MathGreaterThan.outputs[0]) - NodeGroup.links.new(VolumeAbsorption.inputs[0], MathBrightnessMultiply.outputs[0]) - - # Create and Link In/Out to Group Node - # Outputs - group_outputs = NodeGroup.nodes.new('NodeGroupOutput') - group_outputs.location = (600, 395) - NodeGroup.outputs.new('NodeSocketShader', 'shader_out') - NodeGroup.links.new(AddAddAndEmission.outputs[0], group_outputs.inputs['shader_out']) - - # Inputs - group_inputs = NodeGroup.nodes.new('NodeGroupInput') - group_inputs.location = (-1500, -300) - NodeGroup.inputs.new('NodeSocketFloat', 'Density') - NodeGroup.inputs.new('NodeSocketFloat', 'Absorption Multiply') - NodeGroup.inputs.new('NodeSocketColor', 'Absorption Color') - NodeGroup.inputs.new('NodeSocketFloat', 'Scatter Multiply') - NodeGroup.inputs.new('NodeSocketColor', 'Scatter Color') - NodeGroup.inputs.new('NodeSocketFloat', 'Emission Amount') - NodeGroup.inputs.new('NodeSocketFloat', 'Cloud Brightness') - - NodeGroup.links.new(group_inputs.outputs['Density'], MathGreaterThan.inputs[0]) - NodeGroup.links.new(group_inputs.outputs['Absorption Multiply'], MathAbsorptionMultiply.inputs[1]) - NodeGroup.links.new(group_inputs.outputs['Absorption Color'], MathBrightnessMultiply.inputs[0]) - NodeGroup.links.new(group_inputs.outputs['Scatter Multiply'], MathScatterMultiply.inputs[1]) - NodeGroup.links.new(group_inputs.outputs['Scatter Color'], VolumeScatter.inputs[0]) - NodeGroup.links.new(group_inputs.outputs['Emission Amount'], MathEmissionMultiply.inputs[1]) - NodeGroup.links.new(group_inputs.outputs['Cloud Brightness'], MathBrightnessMultiply.inputs[1]) - - if Type == 'CloudGen_TextureProperties': - MathAdd = NodeGroup.nodes.new('ShaderNodeMath') - MathAdd.location = [-200, 0] - MathAdd.operation = 'ADD' - MathDensityMultiply = NodeGroup.nodes.new('ShaderNodeMath') - MathDensityMultiply.location = [-390, 0] - MathDensityMultiply.operation = 'MULTIPLY' - PointDensityRamp = NodeGroup.nodes.new('ShaderNodeValToRGB') - PointDensityRamp.location = [-675, -250] - PointRamp = PointDensityRamp.color_ramp - PElements = PointRamp.elements - PElements[0].position = 0.418 - PElements[0].color = 0, 0, 0, 1 - PElements[1].position = 0.773 - PElements[1].color = 1, 1, 1, 1 - CloudRamp = NodeGroup.nodes.new('ShaderNodeValToRGB') - CloudRamp.location = [-675, 0] - CRamp = CloudRamp.color_ramp - CElements = CRamp.elements - CElements[0].position = 0.527 - CElements[0].color = 0, 0, 0, 1 - CElements[1].position = 0.759 - CElements[1].color = 1, 1, 1, 1 - NoiseTex = NodeGroup.nodes.new('ShaderNodeTexNoise') - NoiseTex.location = [-940, 0] - NoiseTex.inputs['Detail'].default_value = 4 - TexCoord = NodeGroup.nodes.new('ShaderNodeTexCoord') - TexCoord.location = [-1250, 0] - - NodeGroup.links.new(MathAdd.inputs[0], MathDensityMultiply.outputs[0]) - NodeGroup.links.new(MathAdd.inputs[1], PointDensityRamp.outputs[0]) - NodeGroup.links.new(MathDensityMultiply.inputs[0], CloudRamp.outputs[0]) - NodeGroup.links.new(CloudRamp.inputs[0], NoiseTex.outputs[0]) - NodeGroup.links.new(NoiseTex.inputs[0], TexCoord.outputs[3]) - - # Create and Link In/Out to Group Nodes - # Outputs - group_outputs = NodeGroup.nodes.new('NodeGroupOutput') - group_outputs.location = (0, 0) - NodeGroup.outputs.new('NodeSocketFloat', 'Density W_CloudTex') - NodeGroup.links.new(MathAdd.outputs[0], group_outputs.inputs['Density W_CloudTex']) - - # Inputs - group_inputs = NodeGroup.nodes.new('NodeGroupInput') - group_inputs.location = (-1250, -300) - NodeGroup.inputs.new('NodeSocketFloat', 'Scale') - NodeGroup.inputs.new('NodeSocketFloat', 'Point Density In') - NodeGroup.links.new(group_inputs.outputs['Scale'], NoiseTex.inputs['Scale']) - NodeGroup.links.new(group_inputs.outputs['Point Density In'], MathDensityMultiply.inputs[1]) - NodeGroup.links.new(group_inputs.outputs['Point Density In'], PointDensityRamp.inputs[0]) - - return NodeGroup - - -# This routine takes an object and deletes all of the geometry in it -# and adds a bounding box to it. -# It will add or subtract the bound box size by the variable sizeDifference. - -def getMeshandPutinEditMode(view_layer, object): - - # Go into Object Mode - bpy.ops.object.mode_set(mode='OBJECT') - - # Deselect All - bpy.ops.object.select_all(action='DESELECT') - - # Select the object - object.select_set(True) - view_layer.objects.active = object - - # Go into Edit Mode - bpy.ops.object.mode_set(mode='EDIT') - - return object.data - - -def maxAndMinVerts(view_layer, object): - - mesh = getMeshandPutinEditMode(view_layer, object) - verts = mesh.vertices - - # Set the max and min verts to the first vertex on the list - maxVert = [verts[0].co[0], verts[0].co[1], verts[0].co[2]] - minVert = [verts[0].co[0], verts[0].co[1], verts[0].co[2]] - - # Create Max and Min Vertex array for the outer corners of the box - for vert in verts: - # Max vertex - if vert.co[0] > maxVert[0]: - maxVert[0] = vert.co[0] - if vert.co[1] > maxVert[1]: - maxVert[1] = vert.co[1] - if vert.co[2] > maxVert[2]: - maxVert[2] = vert.co[2] - - # Min Vertex - if vert.co[0] < minVert[0]: - minVert[0] = vert.co[0] - if vert.co[1] < minVert[1]: - minVert[1] = vert.co[1] - if vert.co[2] < minVert[2]: - minVert[2] = vert.co[2] - - return [maxVert, minVert] - - -def makeObjectIntoBoundBox(view_layer, objects, sizeDifference, takeFromObject): - # Let's find the max and min of the reference object, - # it can be the same as the destination object - [maxVert, minVert] = maxAndMinVerts(view_layer, takeFromObject) - - # get objects mesh - mesh = getMeshandPutinEditMode(view_layer, objects) - - # Add the size difference to the max size of the box - maxVert[0] = maxVert[0] + sizeDifference - maxVert[1] = maxVert[1] + sizeDifference - maxVert[2] = maxVert[2] + sizeDifference - - # subtract the size difference to the min size of the box - minVert[0] = minVert[0] - sizeDifference - minVert[1] = minVert[1] - sizeDifference - minVert[2] = minVert[2] - sizeDifference - - # Create arrays of verts and faces to be added to the mesh - addVerts = [] - - # X high loop - addVerts.append([maxVert[0], maxVert[1], maxVert[2]]) - addVerts.append([maxVert[0], maxVert[1], minVert[2]]) - addVerts.append([maxVert[0], minVert[1], minVert[2]]) - addVerts.append([maxVert[0], minVert[1], maxVert[2]]) - - # X low loop - addVerts.append([minVert[0], maxVert[1], maxVert[2]]) - addVerts.append([minVert[0], maxVert[1], minVert[2]]) - addVerts.append([minVert[0], minVert[1], minVert[2]]) - addVerts.append([minVert[0], minVert[1], maxVert[2]]) - - # Make the faces of the bounding box. - addFaces = [] - - # Draw a box on paper and number the vertices. - # Use right hand rule to come up with number orders for faces on - # the box (with normals pointing out). - addFaces.append([0, 3, 2, 1]) - addFaces.append([4, 5, 6, 7]) - addFaces.append([0, 1, 5, 4]) - addFaces.append([1, 2, 6, 5]) - addFaces.append([2, 3, 7, 6]) - addFaces.append([0, 4, 7, 3]) - - # Delete all geometry from the object. - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.delete(type='VERT') - - # Must be in object mode for from_pydata to work - bpy.ops.object.mode_set(mode='OBJECT') - - # Add the mesh data. - mesh.from_pydata(addVerts, [], addFaces) - mesh.validate() - - # Update the mesh - mesh.update() - - -def applyScaleRotLoc(view_layer, obj): - # Deselect All - bpy.ops.object.select_all(action='DESELECT') - - # Select the object - obj.select_set(True) - view_layer.objects.active = obj - - bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) - - -def totallyDeleteObject(obj): - bpy.data.objects.remove(obj, do_unlink=True) - - -def makeParent(parentobj, childobj, view_layer): - applyScaleRotLoc(view_layer, parentobj) - applyScaleRotLoc(view_layer, childobj) - childobj.parent = parentobj - - -def addNewObject(collection, name, copyobj): - # avoid creating not needed meshes pro forme - # Create a new object - tempme = copyobj.data - ob_new_data = tempme.copy() - ob_new = bpy.data.objects.new(name, ob_new_data) - ob_new.scale = copyobj.scale - ob_new.location = copyobj.location - - # Link new object to the given scene and select it - collection.objects.link(ob_new) - ob_new.select_set(True) - - return ob_new - - -def getpdensitytexture(object): - - for mslot in object.material_slots: - # Material slot can be empty - mat = getattr(mslot, "material", None) - if mat: - for tslot in mat.texture_slots: - if tslot != 'NoneType': - tex = tslot.texture - if tex.type == 'POINT_DENSITY': - if tex.point_density.point_source == 'PARTICLE_SYSTEM': - return tex - - -def removeParticleSystemFromObj(view_layer, obj): - # Deselect All - bpy.ops.object.select_all(action='DESELECT') - - # Select the object - obj.select_set(True) - view_layer.objects.active = obj - - bpy.ops.object.particle_system_remove() - - # Deselect All - bpy.ops.object.select_all(action='DESELECT') - - -def convertParticlesToMesh(view_layer, particlesobj, destobj, replacemesh): - # Select the Destination object - destobj.select_set(True) - view_layer.objects.active = destobj - - # Go to Edit Mode - bpy.ops.object.mode_set(mode='EDIT', toggle=False) - - # Delete everything in mesh if replace is true - if replacemesh: - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.delete(type='VERT') - - meshPnts = destobj.data - - listCloudParticles = particlesobj.particles - - listMeshPnts = [] - for pTicle in listCloudParticles: - listMeshPnts.append(pTicle.location) - - # Must be in object mode for from_pydata to work - bpy.ops.object.mode_set(mode='OBJECT') - - # Add in the mesh data - meshPnts.from_pydata(listMeshPnts, [], []) - - # Update and Validate the mesh - meshPnts.validate() - meshPnts.update() - - -def combineObjects(view_layer, combined, listobjs): - # scene is the current scene - # combined is the object we want to combine everything into - # listobjs is the list of objects to stick into combined - - # Deselect All - bpy.ops.object.select_all(action='DESELECT') - - # Select the new object. - combined.select_set(True) - view_layer.objects.active = combined - - # Add data - if len(listobjs) > 0: - for i in listobjs: - # Add a modifier - bpy.ops.object.modifier_add(type='BOOLEAN') - - union = combined.modifiers - union[0].name = "AddEmUp" - union[0].object = i - union[0].operation = 'UNION' - - # Apply modifier - bpy.ops.object.modifier_apply(apply_as='DATA', modifier=union[0].name) - - -# Returns the action we want to take -def getActionToDo(obj): - - if not obj or obj.type != 'MESH': - return 'NOT_OBJ_DO_NOTHING' - - elif obj is None: - return 'NO_SELECTION_DO_NOTHING' - - elif "CloudMember" in obj: - if obj["CloudMember"] is not None: - if obj["CloudMember"] == "MainObj": - return 'DEGENERATE' - elif obj["CloudMember"] == "CreatedObj" and len(obj.particle_systems) > 0: - return 'CLOUD_CONVERT_TO_MESH' - else: - return 'CLOUD_DO_NOTHING' - - elif obj.type == 'MESH': - return 'GENERATE' - - else: - return 'DO_NOTHING' - - -class VIEW3D_PT_tools_cloud(Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = 'Create' - bl_label = "Cloud Generator" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - active_obj = context.active_object - layout = self.layout - col = layout.column(align=True) - - WhatToDo = getActionToDo(active_obj) - - if WhatToDo == 'DEGENERATE': - col.operator("cloud.generate_cloud", text="DeGenerate") - - elif WhatToDo == 'CLOUD_CONVERT_TO_MESH': - col.operator("cloud.generate_cloud", text="Convert to Mesh") - - elif WhatToDo == 'NO_SELECTION_DO_NOTHING': - col.label(text="Select one or more") - col.label(text="objects to generate") - col.label(text="a cloud") - - elif WhatToDo == 'CLOUD_DO_NOTHING': - col.label(text="Must select") - col.label(text="bound box") - - elif WhatToDo == 'GENERATE': - col.operator("cloud.generate_cloud", text="Generate Cloud") - - col.prop(context.scene, "cloud_type") - col.prop(context.scene, "cloudsmoothing") - else: - col.label(text="Select one or more", icon="INFO") - col.label(text="objects to generate", icon="BLANK1") - col.label(text="a cloud", icon="BLANK1") - - -class GenerateCloud(Operator): - bl_idname = "cloud.generate_cloud" - bl_label = "Generate Cloud" - bl_description = ("Create a Cloud, Undo a Cloud, or convert to " - "Mesh Cloud depending on selection\n" - "Needs an Active Mesh Object") - bl_options = {"REGISTER", "UNDO"} - - @classmethod - def poll(cls, context): - obj = context.active_object - return (obj and obj.type == 'MESH') - - def execute(self, context): - # Prevent unsupported Execution in Local View modes - space_data = bpy.context.space_data - -# if True in space_data.layers_local_view: -# self.report({'INFO'}, -# "Works with Global Perspective modes only. Operation Cancelled") -# return {'CANCELLED'} - - # Make variable that is the active object selected by user - active_object = context.active_object - - # Make variable scene that is current scene - collection = context.collection - scene = context.scene - view_layer = context.view_layer - - # 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. - - if bpy.context.scene.render.engine == 'BLENDER_RENDER': - numOfPoints = 1.0 - maxNumOfPoints = 100000 - maxPointDensityRadius = 1.5 - scattering = 2.5 - pointDensityRadiusFactor = 1.0 - densityScale = 1.5 - elif bpy.context.scene.render.engine == 'CYCLES': - numOfPoints = .80 - maxNumOfPoints = 100000 - maxPointDensityRadius = 1.0 - scattering = 2.5 - pointDensityRadiusFactor = .37 - densityScale = 1.5 - - # What should we do? - WhatToDo = getActionToDo(active_object) - - if WhatToDo == 'DEGENERATE': - # Degenerate Cloud - mainObj = active_object - - bpy.ops.object.hide_view_clear() - - cloudMembers = active_object.children - createdObjects = [] - definitionObjects = [] - - for member in cloudMembers: - applyScaleRotLoc(view_layer, member) - if member["CloudMember"] == "CreatedObj": - createdObjects.append(member) - else: - definitionObjects.append(member) - - for defObj in definitionObjects: - # Delete cloudmember data from objects - if "CloudMember" in defObj: - del(defObj["CloudMember"]) - - for createdObj in createdObjects: - totallyDeleteObject(createdObj) - - # Delete the blend_data object - totallyDeleteObject(mainObj) - - # Select all of the left over boxes so people can immediately - # press generate again if they want - for eachMember in definitionObjects: - eachMember.display_type = 'SOLID' - eachMember.select_set(True) - eachMember.hide_render = False - - elif WhatToDo == 'CLOUD_CONVERT_TO_MESH': - cloudParticles = active_object.particle_systems.active - - bounds = active_object.parent - - # Create CloudPnts for putting points in # - # Create a new object cloudPnts - cloudPnts = addNewObject(collection, "CloudPoints", bounds) - cloudPnts["CloudMember"] = "CreatedObj" - cloudPnts.display_type = 'WIRE' - cloudPnts.hide_render = True - - makeParent(bounds, cloudPnts, view_layer) - convertParticlesToMesh(view_layer, cloudParticles, cloudPnts, True) - removeParticleSystemFromObj(view_layer, active_object) - - pDensity = getpdensitytexture(bounds) - pDensity.point_density.point_source = 'OBJECT' - pDensity.point_density.object = cloudPnts - - # Let's resize the bound box to be more accurate - how_much_bigger = pDensity.point_density.radius - makeObjectIntoBoundBox(view_layer, bounds, how_much_bigger, cloudPnts) - - 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( - collection, "CloudBounds", - selectedObjects[0] - ) - - bounds.display_type = 'BOUNDS' - bounds.hide_render = False - - # Just add a Definition Property designating this - # as the blend_data 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(view_layer, bounds) - for each in selectedObjects: - applyScaleRotLoc(view_layer, each) - - # Let's combine all of them together. - combineObjects(view_layer, bounds, selectedObjects) - - # Let's add some property info to the objects - for selObj in selectedObjects: - selObj["CloudMember"] = "DefinitionObj" - selObj.name = "DefinitionObj" - selObj.display_type = 'WIRE' - selObj.hide_render = True - selObj.hide = True - makeParent(bounds, selObj, view_layer) - - # Do the same to the 1. object since it is no longer in list. - firstObject["CloudMember"] = "DefinitionObj" - firstObject.name = "DefinitionObj" - firstObject.display_type = 'WIRE' - firstObject.hide_render = True - makeParent(bounds, firstObject, view_layer) - - # Create Cloud for putting Cloud Mesh # - # Create a new object cloud. - cloud = addNewObject(collection, "CloudMesh", bounds) - cloud["CloudMember"] = "CreatedObj" - cloud.display_type = 'WIRE' - cloud.hide_render = True - - makeParent(bounds, cloud, view_layer) - - bpy.ops.object.editmode_toggle() - bpy.ops.mesh.select_all(action='SELECT') - - # Don't subdivide object or smooth if smoothing box not checked. - if scene.cloudsmoothing: - bpy.ops.mesh.subdivide(number_cuts=2, fractal=0, smoothness=1) - 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 # - - # 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.particle_systems.active - cloudParticles.name = "CloudParticles" - cloudParticles.settings.frame_start = 0 - cloudParticles.settings.frame_end = 0 - cloudParticles.settings.emit_from = 'VOLUME' - cloudParticles.settings.lifetime = scene.frame_end - cloudParticles.settings.display_method = 'DOT' - cloudParticles.settings.render_type = 'NONE' - cloudParticles.settings.distribution = 'RAND' - cloudParticles.settings.physics_type = 'NEWTON' - cloudParticles.settings.normal_factor = 0 - - # Gravity does not affect the particle system - eWeights = cloudParticles.settings.effector_weights - eWeights.gravity = 0 - - # Create Volume Material # - # Deselect All - bpy.ops.object.select_all(action='DESELECT') - - # Select the object. - bounds.select_set(True) - view_layer.objects.active = bounds - - # Turn bounds object into a box. Use itself as a reference - makeObjectIntoBoundBox(view_layer, bounds, 1.0, bounds) - - # 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 = bpy.data.materials.new("CloudMaterial") - bpy.ops.object.material_slot_add() - bounds.material_slots[0].material = cloudMaterial - - # Set time - scene.frame_current = 1 - - # Set Up Material for Blender Internal - if bpy.context.scene.render.engine == 'BLENDER_RENDER': - # 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.0, 3.0, 3.0 - mVolume.step_size = 0.1 - mVolume.use_light_cache = True - mVolume.cache_resolution = 45 - - # Add a texture - # vMaterialTextureSlots = cloudMaterial.texture_slots # UNUSED - cloudtex = bpy.data.textures.new("CloudTex", type='CLOUDS') - cloudtex.noise_type = 'HARD_NOISE' - cloudtex.noise_scale = 2 - mtex = cloudMaterial.texture_slots.add() - mtex.texture = cloudtex - mtex.texture_coords = 'ORCO' - mtex.use_map_color_diffuse = True - - # Set time - scene.frame_current = 1 - - # Add a Point Density texture - pDensity = bpy.data.textures.new("CloudPointDensity", 'POINT_DENSITY') - - mtex = cloudMaterial.texture_slots.add() - mtex.texture = pDensity - mtex.texture_coords = 'GLOBAL' - mtex.use_map_density = True - mtex.use_rgb_to_intensity = True - mtex.texture_coords = 'GLOBAL' - - pDensity.point_density.vertex_cache_space = 'WORLD_SPACE' - pDensity.point_density.use_turbulence = True - pDensity.point_density.noise_basis = 'VORONOI_F2' - pDensity.point_density.turbulence_depth = 3 - - pDensity.use_color_ramp = True - pRamp = pDensity.color_ramp - # pRamp.use_interpolation = 'LINEAR' - pRampElements = pRamp.elements - # pRampElements[1].position = .9 - # pRampElements[1].color = 0.18, 0.18, 0.18, 0.8 - bpy.ops.texture.slot_move(type='UP') - - # Set Up Material for Cycles Engine - elif bpy.context.scene.render.engine == 'CYCLES': - VolumePropertiesGroup = CreateNodeGroup('CloudGen_VolumeProperties') - CloudTexPropertiesGroup = CreateNodeGroup('CloudGen_TextureProperties') - - cloudMaterial.name = "CloudMaterial" - # Add a texture - cloudtex = bpy.data.textures.new("CloudTex", type='CLOUDS') - cloudtex.noise_type = 'HARD_NOISE' - cloudtex.noise_scale = 2 - - cloudMaterial.use_nodes = True - cloudTree = cloudMaterial.node_tree - cloudMatNodes = cloudTree.nodes - cloudMatNodes.clear() - - outputNode = cloudMatNodes.new('ShaderNodeOutputMaterial') - outputNode.location = (200, 300) - - tranparentNode = cloudMatNodes.new('ShaderNodeBsdfTransparent') - tranparentNode.location = (0, 300) - - volumeGroup = cloudMatNodes.new("ShaderNodeGroup") - volumeGroup.node_tree = VolumePropertiesGroup - volumeGroup.location = (0, 150) - - cloudTexGroup = cloudMatNodes.new("ShaderNodeGroup") - cloudTexGroup.node_tree = CloudTexPropertiesGroup - cloudTexGroup.location = (-200, 150) - - PointDensityNode = cloudMatNodes.new("ShaderNodeTexPointDensity") - PointDensityNode.location = (-400, 150) - PointDensityNode.resolution = 100 - PointDensityNode.space = 'OBJECT' - PointDensityNode.interpolation = 'Linear' - # PointDensityNode.color_source = 'CONSTANT' - - cloudTree.links.new(outputNode.inputs[0], tranparentNode.outputs[0]) - cloudTree.links.new(outputNode.inputs[1], volumeGroup.outputs[0]) - cloudTree.links.new(volumeGroup.inputs[0], cloudTexGroup.outputs[0]) - cloudTree.links.new(cloudTexGroup.inputs[1], PointDensityNode.outputs[1]) - - # 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 - if numParticles < 10000: - numParticles = int(numParticles + 15 * volumeBoundBox) - - # Set the number of particles according to the volume of bounds - cloudParticles.settings.count = numParticles - - PDensityRadius = (.00013764 * volumeBoundBox + .3989) * pointDensityRadiusFactor - - if bpy.context.scene.render.engine == 'BLENDER_RENDER': - pDensity.point_density.radius = PDensityRadius - - if pDensity.point_density.radius > maxPointDensityRadius: - pDensity.point_density.radius = maxPointDensityRadius - - elif bpy.context.scene.render.engine == 'CYCLES': - PointDensityNode.radius = PDensityRadius - - if PDensityRadius > maxPointDensityRadius: - PointDensityNode.radius = maxPointDensityRadius - - # Set time to 1. - scene.frame_current = 1 - - if not scene.cloudparticles: - # Create CloudPnts for putting points in # - # Create a new object cloudPnts - cloudPnts = addNewObject(collection, "CloudPoints", bounds) - cloudPnts["CloudMember"] = "CreatedObj" - cloudPnts.display_type = 'WIRE' - cloudPnts.hide_render = True - - makeParent(bounds, cloudPnts, view_layer) - convertParticlesToMesh(view_layer, cloudParticles, cloudPnts, True) - - # Add a modifier. - bpy.ops.object.modifier_add(type='DISPLACE') - - cldPntsModifiers = cloudPnts.modifiers - cldPntsModifiers[0].name = "CloudPnts" - cldPntsModifiers[0].texture = cloudtex - cldPntsModifiers[0].texture_coords = 'OBJECT' - cldPntsModifiers[0].texture_coords_object = cloud - cldPntsModifiers[0].strength = -1.4 - - # Apply modifier - bpy.ops.object.modifier_apply(apply_as='DATA', modifier=cldPntsModifiers[0].name) - - if bpy.context.scene.render.engine == 'BLENDER_RENDER': - pDensity.point_density.point_source = 'OBJECT' - pDensity.point_density.object = cloudPnts - - elif bpy.context.scene.render.engine == 'CYCLES': - PointDensityNode.point_source = 'OBJECT' - PointDensityNode.object = cloudPnts - - removeParticleSystemFromObj(view_layer, cloud) - - else: - if bpy.context.scene.render.engine == 'BLENDER_RENDER': - pDensity.point_density.point_source = 'PARTICLE_SYSTEM' - pDensity.point_density.object = cloud - pDensity.point_density.particle_system = cloudParticles - - elif bpy.context.scene.render.engine == 'CYCLES': - PointDensityNode.point_source = 'PARTICLE_SYSTEM' - PointDensityNode.particle_system = cloudPnts - - if bpy.context.scene.render.engine == 'BLENDER_RENDER': - if scene.cloud_type == '1': # Cumulous - mVolume.density_scale = 2.22 - pDensity.point_density.turbulence_depth = 10 - pDensity.point_density.turbulence_strength = 6.3 - pDensity.point_density.turbulence_scale = 2.9 - pRampElements[1].position = .606 - pDensity.point_density.radius = pDensity.point_density.radius + 0.1 - - elif scene.cloud_type == '2': # Cirrus - pDensity.point_density.turbulence_strength = 22 - mVolume.transmission_color = 3.5, 3.5, 3.5 - mVolume.scattering = 0.13 - - elif scene.cloud_type == '3': # Explosion - mVolume.emission = 1.42 - mtex.use_rgb_to_intensity = False - pRampElements[0].position = 0.825 - pRampElements[0].color = 0.119, 0.119, 0.119, 1 - pRampElements[1].position = .049 - pRampElements[1].color = 1.0, 1.0, 1.0, 0 - pDensity.point_density.turbulence_strength = 1.5 - pRampElement1 = pRampElements.new(.452) - pRampElement1.color = 0.814, 0.112, 0, 1 - pRampElement2 = pRampElements.new(.234) - pRampElement2.color = 0.814, 0.310, 0.002, 1 - pRampElement3 = pRampElements.new(0.669) - pRampElement3.color = 0.0, 0.0, 0.040, 1 - - elif bpy.context.scene.render.engine == 'CYCLES': - - volumeGroup.inputs['Absorption Multiply'].default_value = 50 - volumeGroup.inputs['Absorption Color'].default_value = (1.0, 1.0, 1.0, 1.0) - volumeGroup.inputs['Scatter Multiply'].default_value = 30 - volumeGroup.inputs['Scatter Color'].default_value = (.58, .58, .58, 1.0) - volumeGroup.inputs['Emission Amount'].default_value = .1 - volumeGroup.inputs['Cloud Brightness'].default_value = 1.3 - noiseCloudScale = volumeBoundBox * (-.001973) + 5.1216 - if noiseCloudScale < .05: - noiseCloudScale = .05 - cloudTexGroup.inputs['Scale'].default_value = noiseCloudScale - - # to cloud to view in cycles in render mode we need to hide geometry meshes... - firstObject.hide_viewport = True - cloud.hide_viewport = True - - # Select the object. - bounds.select_set(True) - view_layer.objects.active = bounds - - # Let's resize the bound box to be more accurate. - how_much_bigger = PDensityRadius + 0.1 - - # If it's a particle cloud use cloud mesh if otherwise use point mesh - if not scene.cloudparticles: - makeObjectIntoBoundBox(view_layer, bounds, how_much_bigger, cloudPnts) - else: - makeObjectIntoBoundBox(view_layer, bounds, how_much_bigger, cloud) - - cloud_string = "Cumulous" if scene.cloud_type == '1' else "Cirrus" if \ - scene.cloud_type == '2' else "Stratus" if \ - scene.cloud_type == '0' else "Explosion" - - self.report({'INFO'}, - "Created the cloud of type {}".format(cloud_string)) - - return {'FINISHED'} - -# List The Classes # - -classes = ( - VIEW3D_PT_tools_cloud, - GenerateCloud - ) - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - - bpy.types.Scene.cloudparticles = BoolProperty( - name="Particles", - description="Generate Cloud as Particle System", - default=False - ) - bpy.types.Scene.cloudsmoothing = BoolProperty( - name="Smoothing", - description="Smooth Resultant Geometry From Gen Cloud Operation", - default=True - ) - bpy.types.Scene.cloud_type = EnumProperty( - name="Type", - description="Select the type of cloud to create with material settings", - items=[("0", "Stratus", "Generate Stratus (foggy) Cloud"), - ("1", "Cumulous", "Generate Cumulous (puffy) Cloud"), - ("2", "Cirrus", "Generate Cirrus (wispy) Cloud"), - ("3", "Explosion", "Generate Explosion"), - ], - default='0' - ) - - -def unregister(): - for cls in reversed(classes): - bpy.utils.unregister_class(cls) - - del bpy.types.Scene.cloudparticles - del bpy.types.Scene.cloudsmoothing - del bpy.types.Scene.cloud_type - - -if __name__ == "__main__": - register()