[Bf-extensions-cvs] [8a451e74] master: real snow: initial commit 2.83 T74122

meta-androcto noreply at git.blender.org
Sat Mar 7 00:39:29 CET 2020


Commit: 8a451e74b6822ff5f00536a385329e51e01f7c41
Author: meta-androcto
Date:   Sat Mar 7 10:39:04 2020 +1100
Branches: master
https://developer.blender.org/rBA8a451e74b6822ff5f00536a385329e51e01f7c41

real snow: initial commit 2.83 T74122

===================================================================

A	real_snow.py

===================================================================

diff --git a/real_snow.py b/real_snow.py
new file mode 100644
index 00000000..f1091b2d
--- /dev/null
+++ b/real_snow.py
@@ -0,0 +1,415 @@
+# ##### 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 #####
+
+bl_info = {
+    "name": "Real Snow",
+    "description": "Generate snow mesh",
+    "author": "Wolf <wolf.art3d at gmail.com>",
+    "version": (1, 1),
+    "blender": (2, 83, 0),
+    "location": "View 3D > Properties Panel",
+    "doc_url": "https://github.com/macio97/Real-Snow",
+    "tracker_url": "https://github.com/macio97/Real-Snow/issues",
+    "support": "COMMUNITY",
+    "category": "Object",
+    }
+
+
+# Libraries
+import math
+import os
+import random
+import time
+
+import bpy
+import bmesh
+from bpy.props import BoolProperty, FloatProperty, IntProperty, PointerProperty
+from bpy.types import Operator, Panel, PropertyGroup
+from mathutils import Vector
+
+
+# Panel
+class REAL_PT_snow(Panel):
+    bl_space_type = "VIEW_3D"
+    bl_context = "objectmode"
+    bl_region_type = "UI"
+    bl_label = "Snow"
+    bl_category = "Real Snow"
+
+    def draw(self, context):
+        scn = context.scene
+        settings = scn.snow
+        layout = self.layout
+
+        col = layout.column(align=True)
+        col.prop(settings, 'coverage', slider=True)
+        col.prop(settings, 'height')
+
+        layout.use_property_split = True
+        layout.use_property_decorate = False
+        flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=True)
+        col = flow.column()
+        col.prop(settings, 'vertices')
+
+        row = layout.row(align=True)
+        row.scale_y = 1.5
+        row.operator("snow.create", text="Add Snow", icon="FREEZE")
+
+
+class SNOW_OT_Create(Operator):
+    bl_idname = "snow.create"
+    bl_label = "Create Snow"
+    bl_description = "Create snow"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    @classmethod
+    def poll(cls, context) -> bool:
+        return bool(context.selected_objects)
+
+    def execute(self, context):
+        coverage = context.scene.snow.coverage
+        height = context.scene.snow.height
+        vertices = context.scene.snow.vertices
+
+        # get list of selected objects except non-mesh objects
+        input_objects = [obj for obj in context.selected_objects if obj.type == 'MESH']
+        snow_list = []
+        # start UI progress bar
+        length = len(input_objects)
+        context.window_manager.progress_begin(0, 10)
+        timer=0
+        for obj in input_objects:
+            # timer
+            context.window_manager.progress_update(timer)
+            # duplicate mesh
+            bpy.ops.object.select_all(action='DESELECT')
+            obj.select_set(True)
+            context.view_layer.objects.active = obj
+            object_eval = obj.evaluated_get(context.view_layer.depsgraph)
+            mesh_eval = bpy.data.meshes.new_from_object(object_eval)
+            snow_object = bpy.data.objects.new("Snow", mesh_eval)
+            snow_object.matrix_world = obj.matrix_world
+            context.collection.objects.link(snow_object)
+            bpy.ops.object.select_all(action='DESELECT')
+            context.view_layer.objects.active = snow_object
+            snow_object.select_set(True)
+            bpy.ops.object.mode_set(mode = 'EDIT')
+            bm_orig = bmesh.from_edit_mesh(snow_object.data)
+            bm_copy = bm_orig.copy()
+            bm_copy.transform(obj.matrix_world)
+            bm_copy.normal_update()
+            # get faces data
+            delete_faces(vertices, bm_copy, snow_object)
+            ballobj = add_metaballs(context, height, snow_object)
+            context.view_layer.objects.active = snow_object
+            surface_area = area(snow_object)
+            snow = add_particles(context, surface_area, height, coverage, snow_object, ballobj)
+            add_modifiers(snow)
+            # place inside collection
+            context.view_layer.active_layer_collection = context.view_layer.layer_collection
+            if "Snow" not in context.scene.collection.children:
+                coll = bpy.data.collections.new("Snow")
+                context.scene.collection.children.link(coll)
+            else:
+                coll = bpy.data.collections["Snow"]
+            coll.objects.link(snow)
+            context.view_layer.layer_collection.collection.objects.unlink(snow)
+            add_material(snow)
+            # parent with object
+            snow.parent = obj
+            snow.matrix_parent_inverse = obj.matrix_world.inverted()
+            # add snow to list
+            snow_list.append(snow)
+            # update progress bar
+            timer += 0.1 / length
+        # select created snow meshes
+        for s in snow_list:
+            s.select_set(True)
+        # end progress bar
+        context.window_manager.progress_end()
+
+        return {'FINISHED'}
+
+
+def add_modifiers(snow):
+    bpy.ops.object.transform_apply(location=False, scale=True, rotation=False)
+    # decimate the mesh to get rid of some visual artifacts
+    snow.modifiers.new("Decimate", 'DECIMATE')
+    snow.modifiers["Decimate"].ratio = 0.5
+    snow.modifiers.new("Subdiv", "SUBSURF")
+    snow.modifiers["Subdiv"].render_levels = 1
+    snow.modifiers["Subdiv"].quality = 1
+    snow.cycles.use_adaptive_subdivision = True
+
+
+def add_particles(context, surface_area: float, height: float, coverage: float, snow_object: bpy.types.Object, ballobj: bpy.types.Object):
+    # approximate the number of particles to be emitted
+    number = int(surface_area*50*(height**-2)*((coverage/100)**2))
+    bpy.ops.object.particle_system_add()
+    particles = snow_object.particle_systems[0]
+    psettings = particles.settings
+    psettings.type = 'HAIR'
+    psettings.render_type = 'OBJECT'
+    # generate random number for seed
+    random_seed = random.randint(0, 1000)
+    particles.seed = random_seed
+    # set particles object
+    psettings.particle_size = height
+    psettings.instance_object = ballobj
+    psettings.count = number
+    # convert particles to mesh
+    bpy.ops.object.select_all(action='DESELECT')
+    context.view_layer.objects.active = ballobj
+    ballobj.select_set(True)
+    bpy.ops.object.convert(target='MESH')
+    snow = bpy.context.active_object
+    snow.scale = [0.09, 0.09, 0.09]
+    bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
+    bpy.ops.object.select_all(action='DESELECT')
+    snow_object.select_set(True)
+    bpy.ops.object.delete()
+    snow.select_set(True)
+    return snow
+
+
+def add_metaballs(context, height: float, snow_object: bpy.types.Object) -> bpy.types.Object:
+    ball_name = "SnowBall"
+    ball = bpy.data.metaballs.new(ball_name)
+    ballobj = bpy.data.objects.new(ball_name, ball)
+    bpy.context.scene.collection.objects.link(ballobj)
+    # these settings have proven to work on a large amount of scenarios
+    ball.resolution = 0.7*height+0.3
+    ball.threshold = 1.3
+    element = ball.elements.new()
+    element.radius = 1.5
+    element.stiffness = 0.75
+    ballobj.scale = [0.09, 0.09, 0.09]
+    return ballobj
+
+
+def delete_faces(vertices, bm_copy, snow_object: bpy.types.Object):
+    # find upper faces
+    if vertices:
+        selected_faces = [face.index for face in bm_copy.faces if face.select]
+    # based on a certain angle, find all faces not pointing up
+    down_faces = [face.index for face in bm_copy.faces if Vector((0, 0, -1.0)).angle(face.normal, 4.0) < (math.pi/2.0+0.5)]
+    bm_copy.free()
+    bpy.ops.mesh.select_all(action='DESELECT')
+    # select upper faces
+    mesh = bmesh.from_edit_mesh(snow_object.data)
+    for face in mesh.faces:
+        if vertices:
+            if not face.index in selected_faces:
+                face.select = True
+        if face.index in down_faces:
+            face.select = True
+    # delete unneccessary faces
+    faces_select = [face for face in mesh.faces if face.select]
+    bmesh.ops.delete(mesh, geom=faces_select, context='FACES_KEEP_BOUNDARY')
+    mesh.free()
+    bpy.ops.object.mode_set(mode = 'OBJECT')
+
+
+def area(obj: bpy.types.Object) -> float:
+    bm_obj = bmesh.new()
+    bm_obj.from_mesh(obj.data)
+    bm_obj.transform(obj.matrix_world)
+    area = sum(face.calc_area() for face in bm_obj.faces)
+    bm_obj.free
+    return area
+
+
+def add_material(obj: bpy.types.Object):
+    mat_name = "Snow"
+    # if material doesn't exist, create it
+    if mat_name in bpy.data.materials:
+	    bpy.data.materials[mat_name].name = mat_name+".001"
+    mat = bpy.data.materials.new(mat_name)
+    mat.use_nodes = True
+    nodes = mat.node_tree.nodes
+    # delete all nodes
+    for node in nodes:
+	    nodes.remove(node)
+    # add nodes
+    output = nodes.new('ShaderNodeOutputMaterial')
+    principled = nodes.new('ShaderNodeBsdfPrincipled')
+    vec_math = nodes.new('ShaderNodeVectorMath')
+    com_xyz = nodes.new('ShaderNodeCombineXYZ')
+    dis = nodes.new('ShaderNodeDisplacement')
+    mul1 = nodes.new('ShaderNodeMath')
+    add1 = nodes.new('ShaderNodeMath')
+    add2 = nodes.new('ShaderNodeMath')
+    mul2 = nodes.new('ShaderNodeMath')
+    mul3 = nodes.new('ShaderNodeMath')
+    ramp1 = nodes.new('ShaderNodeValToRGB')
+    ramp2 = nodes.new('ShaderNodeValToRGB')
+    ramp3 = nodes.new('ShaderNodeValToRGB')
+    vor = nodes.new('ShaderNodeTexVoronoi')
+    noise1 = nodes.new('ShaderNodeTexNoise')
+    noise2 = nodes.new('ShaderNodeTexNoise')
+    noi

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list