forked from blender/blender
WIP: uv-simple-select #1
@ -36,67 +36,135 @@ class ObjectModeOperator:
|
|||||||
|
|
||||||
|
|
||||||
class QuickFur(ObjectModeOperator, Operator):
|
class QuickFur(ObjectModeOperator, Operator):
|
||||||
"""Add fur setup to the selected objects"""
|
"""Add a fur setup to the selected objects"""
|
||||||
bl_idname = "object.quick_fur"
|
bl_idname = "object.quick_fur"
|
||||||
bl_label = "Quick Fur"
|
bl_label = "Quick Fur"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
density: EnumProperty(
|
density: EnumProperty(
|
||||||
name="Fur Density",
|
name="Density",
|
||||||
items=(
|
items=(
|
||||||
('LIGHT', "Light", ""),
|
('LOW', "Low", ""),
|
||||||
('MEDIUM', "Medium", ""),
|
('MEDIUM', "Medium", ""),
|
||||||
('HEAVY', "Heavy", ""),
|
('HIGH', "High", ""),
|
||||||
),
|
),
|
||||||
default='MEDIUM',
|
default='MEDIUM',
|
||||||
)
|
)
|
||||||
view_percentage: IntProperty(
|
|
||||||
name="View %",
|
|
||||||
min=1, max=100,
|
|
||||||
soft_min=1, soft_max=100,
|
|
||||||
default=10,
|
|
||||||
)
|
|
||||||
length: FloatProperty(
|
length: FloatProperty(
|
||||||
name="Length",
|
name="Length",
|
||||||
min=0.001, max=100,
|
min=0.001, max=100,
|
||||||
soft_min=0.01, soft_max=10,
|
soft_min=0.01, soft_max=10,
|
||||||
default=0.1,
|
default=0.1,
|
||||||
|
subtype='DISTANCE'
|
||||||
|
)
|
||||||
|
radius: FloatProperty(
|
||||||
|
name="Hair Radius",
|
||||||
|
min=0.0, max=10,
|
||||||
|
soft_min=0.0001, soft_max=0.1,
|
||||||
|
default=0.001,
|
||||||
|
subtype='DISTANCE'
|
||||||
|
)
|
||||||
|
view_percentage: FloatProperty(
|
||||||
|
name="View Percentage",
|
||||||
|
min=0.0, max=1.0,
|
||||||
|
default=1.0,
|
||||||
|
subtype='FACTOR'
|
||||||
|
)
|
||||||
|
apply_hair_guides: BoolProperty(
|
||||||
|
name="Apply Hair Guides",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
use_noise: BoolProperty(
|
||||||
|
name="Noise",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
use_frizz: BoolProperty(
|
||||||
|
name="Frizz",
|
||||||
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
fake_context = context.copy()
|
import os
|
||||||
mesh_objects = [obj for obj in context.selected_objects
|
mesh_objects = [obj for obj in context.selected_objects if obj.type == 'MESH']
|
||||||
if obj.type == 'MESH']
|
|
||||||
|
|
||||||
if not mesh_objects:
|
if not mesh_objects:
|
||||||
self.report({'ERROR'}, "Select at least one mesh object")
|
self.report({'ERROR'}, "Select at least one mesh object")
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
mat = bpy.data.materials.new("Fur Material")
|
if self.density == 'LOW':
|
||||||
|
count = 1000
|
||||||
|
elif self.density == 'MEDIUM':
|
||||||
|
count = 10000
|
||||||
|
elif self.density == 'HIGH':
|
||||||
|
count = 100000
|
||||||
|
|
||||||
for obj in mesh_objects:
|
node_groups_to_append = {"Generate Hair Curves", "Set Hair Curve Profile", "Interpolate Hair Curves"}
|
||||||
fake_context["object"] = obj
|
if self.use_noise:
|
||||||
bpy.ops.object.particle_system_add(fake_context)
|
node_groups_to_append.add("Hair Curves Noise")
|
||||||
|
if self.use_frizz:
|
||||||
|
node_groups_to_append.add("Frizz Hair Curves")
|
||||||
|
assets_directory = os.path.join(bpy.utils.system_resource('DATAFILES'),
|
||||||
|
"assets",
|
||||||
|
"geometry_nodes",
|
||||||
|
"procedural_hair_node_assets.blend",
|
||||||
|
"NodeTree")
|
||||||
|
for name in node_groups_to_append:
|
||||||
|
bpy.ops.wm.append(directory=assets_directory,
|
||||||
|
filename=name,
|
||||||
|
use_recursive=True,
|
||||||
|
do_reuse_local_id=True)
|
||||||
|
generate_group = bpy.data.node_groups["Generate Hair Curves"]
|
||||||
|
interpolate_group = bpy.data.node_groups["Interpolate Hair Curves"]
|
||||||
|
radius_group = bpy.data.node_groups["Set Hair Curve Profile"]
|
||||||
|
noise_group = bpy.data.node_groups["Hair Curves Noise"] if self.use_noise else None
|
||||||
|
frizz_group = bpy.data.node_groups["Frizz Hair Curves"] if self.use_frizz else None
|
||||||
|
|
||||||
psys = obj.particle_systems[-1]
|
material = bpy.data.materials.new("Fur Material")
|
||||||
psys.settings.type = 'HAIR'
|
|
||||||
|
|
||||||
if self.density == 'LIGHT':
|
for mesh_object in mesh_objects:
|
||||||
psys.settings.count = 100
|
mesh = mesh_object.data
|
||||||
elif self.density == 'MEDIUM':
|
with context.temp_override(active_object=mesh_object):
|
||||||
psys.settings.count = 1000
|
bpy.ops.object.curves_empty_hair_add()
|
||||||
elif self.density == 'HEAVY':
|
curves_object = context.active_object
|
||||||
psys.settings.count = 10000
|
curves = curves_object.data
|
||||||
|
curves.materials.append(material)
|
||||||
|
|
||||||
psys.settings.child_nbr = self.view_percentage
|
area = 0.0
|
||||||
psys.settings.hair_length = self.length
|
for poly in mesh.polygons:
|
||||||
psys.settings.use_strand_primitive = True
|
area += poly.area
|
||||||
psys.settings.use_hair_bspline = True
|
density = count / area
|
||||||
psys.settings.child_type = 'INTERPOLATED'
|
|
||||||
psys.settings.tip_radius = 0.25
|
|
||||||
|
|
||||||
obj.data.materials.append(mat)
|
generate_modifier = curves_object.modifiers.new(name="Generate", type='NODES')
|
||||||
psys.settings.material = len(obj.data.materials)
|
generate_modifier.node_group = generate_group
|
||||||
|
generate_modifier["Input_2"] = mesh_object
|
||||||
|
generate_modifier["Input_18_attribute_name"] = curves.surface_uv_map
|
||||||
|
generate_modifier["Input_20"] = self.length
|
||||||
|
generate_modifier["Input_22"] = material
|
||||||
|
generate_modifier["Input_15"] = density * 0.01
|
||||||
|
curves_object.modifiers.move(1, 0)
|
||||||
|
|
||||||
|
radius_modifier = curves_object.modifiers.new(name="Set Hair Curve Profile", type='NODES')
|
||||||
|
radius_modifier.node_group = radius_group
|
||||||
|
radius_modifier["Input_3"] = self.radius
|
||||||
|
|
||||||
|
interpolate_modifier = curves_object.modifiers.new(name="Interpolate Hair Curves", type='NODES')
|
||||||
|
interpolate_modifier.node_group = interpolate_group
|
||||||
|
interpolate_modifier["Input_2"] = mesh_object
|
||||||
|
interpolate_modifier["Input_18_attribute_name"] = curves.surface_uv_map
|
||||||
|
interpolate_modifier["Input_15"] = density
|
||||||
|
interpolate_modifier["Input_17"] = self.view_percentage
|
||||||
|
interpolate_modifier["Input_24"] = True
|
||||||
|
|
||||||
|
if noise_group:
|
||||||
|
noise_modifier = curves_object.modifiers.new(name="Hair Curves Noise", type='NODES')
|
||||||
|
noise_modifier.node_group = noise_group
|
||||||
|
|
||||||
|
if frizz_group:
|
||||||
|
frizz_modifier = curves_object.modifiers.new(name="Frizz Hair Curves", type='NODES')
|
||||||
|
frizz_modifier.node_group = frizz_group
|
||||||
|
|
||||||
|
if self.apply_hair_guides:
|
||||||
|
with context.temp_override(object=curves_object):
|
||||||
|
bpy.ops.object.modifier_apply(modifier=generate_modifier.name)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@ -2117,6 +2117,7 @@ class VIEW3D_MT_curve_add(Menu):
|
|||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
layout.operator("object.curves_empty_hair_add", text="Empty Hair", icon='CURVES_DATA')
|
layout.operator("object.curves_empty_hair_add", text="Empty Hair", icon='CURVES_DATA')
|
||||||
|
layout.operator("object.quick_fur", text="Fur", icon='CURVES_DATA')
|
||||||
|
|
||||||
experimental = context.preferences.experimental
|
experimental = context.preferences.experimental
|
||||||
if experimental.use_new_curves_tools:
|
if experimental.use_new_curves_tools:
|
||||||
|
Loading…
Reference in New Issue
Block a user