Skip to content

Instantly share code, notes, and snippets.

@Dangetsu
Created January 2, 2023 15:56
Show Gist options
  • Save Dangetsu/3136c6aad1ad42b75d5cb0c1a16b2095 to your computer and use it in GitHub Desktop.
Save Dangetsu/3136c6aad1ad42b75d5cb0c1a16b2095 to your computer and use it in GitHub Desktop.
import bpy
import math
import functools
import fnmatch
from mathutils import Vector, Matrix
# ===========================================================
# Function: select vertices between 2 points
# ===========================================================
def get_verts_in_line(v1, v2, sailMsh):
threshold = 0.001
vec = (v2.co - v1.co).normalized()
return [v.index for v in sailMsh.vertices if ((v.co-v1.co).normalized()-vec).length < threshold]
# ===========================================================
# consts
# ===========================================================
wind_dir = 'l' # wind direction (left(l), right(r) or center(c))
type = 't' # t/v - triangle, d/f/s/g - plane
sail_name_prefix = 'sail_'
vg_pin_sail_name = 'PinSail'
vg_up_down_sail_name = 'UpDownSail'
# ===========================================================
s1 = bpy.data.objects['Static1'].matrix_world.translation
s2 = bpy.data.objects['Static2'].matrix_world.translation
s3 = bpy.data.objects['Movable1'].matrix_world.translation
#s4 = bpy.data.objects['Movable2'].matrix_world.translation
if type == 't' or type == 'v':
vertices = [s1, s2, s3]
edges = []
faces = [(0, 1, 2)]
cloth_name = 'sail'
elif type == 'd' or type == 'f' or type == 's' or type == 'g':
vertices = [s1, s2, s3, s4]
edges = []
faces = [(0, 1, 2, 3)]
else:
raise ValueError('Type is invalid')
new_mesh = bpy.data.meshes.new('sail_mesh')
new_mesh.from_pydata(vertices, edges, faces)
new_mesh.update()
new_object = bpy.data.objects.new(sail_name_prefix, new_mesh)
sail_collection = bpy.data.collections['Tartane1']
sail_collection.objects.link(new_object)
### Resize
sail_obj = bpy.context.scene.objects[new_object.name] # Get the object
bpy.ops.object.select_all(action='DESELECT') # Deselect all objects
bpy.context.view_layer.objects.active = sail_obj # Make the cube the active object
sail_obj.select_set(True)
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.subdivide(number_cuts=50)
bpy.ops.object.mode_set(mode="OBJECT")
################
# Define vertex index
index_s1 = 0 # hard point
index_s2 = 1 # hard point
index_s3 = 2 # soft point
index_s4 = 3 # soft point (only for rectangular sail)
sk = new_object.shape_key_add(name='Basis')
sk.interpolation = 'KEY_LINEAR'
sk = new_object.shape_key_add(name='Deform')
sk.interpolation = 'KEY_LINEAR'
if type == 't' or type == 'v':
indicesBetweenPoints = get_verts_in_line(new_object.data.vertices[index_s2], new_object.data.vertices[index_s3], new_object.data)
for index in indicesBetweenPoints:
sk.data[index].co = s2
sk.data[index_s3].co = s2
elif type == 'd' or type == 'f' or type == 's' or type == 'g':
indicesBetweenPoints = get_verts_in_line(new_object.data.vertices[index_s2], new_object.data.vertices[index_s3], new_object.data)
for index in indicesBetweenPoints:
sk.data[index].co = s2
sk.data[index_s3].co = s2
indicesBetweenPoints2 = get_verts_in_line(new_object.data.vertices[index_s1], new_object.data.vertices[index_s4], new_object.data)
for index in indicesBetweenPoints2:
sk.data[index].co = s1
sk.data[index_s4].co = s1
### Create bone
armature = bpy.data.armatures.new("SailArm")
rig = bpy.data.objects.new("SailArmRig", armature)
sail_collection.objects.link(rig)
bpy.context.view_layer.objects.active = rig
bpy.ops.object.mode_set(mode="EDIT")
if type == 't' or type == 'v':
current_bone = armature.edit_bones.new('Bone')
current_bone.head = s3
current_bone.tail = s2
elif type == 'd' or type == 'f' or type == 's' or type == 'g':
current_bone = armature.edit_bones.new('Bone')
current_bone.head = s3
current_bone.tail = s2
current_bone2 = armature.edit_bones.new('Bone2')
current_bone2.head = s4
current_bone2.tail = s1
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.select_all(action='DESELECT')
new_object.select_set(True)
rig.select_set(True)
bpy.context.view_layer.objects.active = rig
bpy.ops.object.parent_set(type='ARMATURE')
### Clothing
sail_obj = bpy.data.objects[new_object.name]
sail_mesh = sail_obj.data
# Define Vertex Group
sail_obj.vertex_groups.new(name = vg_pin_sail_name)
sail_obj.vertex_groups.new(name = vg_up_down_sail_name)
indices_between_hard_points = get_verts_in_line(sail_mesh.vertices[index_s1], sail_mesh.vertices[index_s2], sail_mesh)
if type == 't' or type == 'v':
indices_hard = [index_s1, index_s2, index_s3] + indices_between_hard_points
indices_between_move_and_hard_points = get_verts_in_line(sail_mesh.vertices[index_s2], sail_mesh.vertices[index_s3], sail_mesh)
indices_move_and_hard = [index_s1, index_s2, index_s3] + indices_between_hard_points + indices_between_move_and_hard_points
elif type == 'd' or type == 'f' or type == 's' or type == 'g':
indices_hard = [index_s1, index_s2, index_s3, index_s4] + indices_between_hard_points
indices_between_move_and_hard_points = get_verts_in_line(sail_mesh.vertices[index_s2], sail_mesh.vertices[index_s3], sail_mesh)
indices_between_move_and_hard_points2 = get_verts_in_line(sail_mesh.vertices[index_s1], sail_mesh.vertices[index_s4], sail_mesh)
indices_move_and_hard = [index_s1, index_s2, index_s3, index_s4] + indices_between_hard_points + indices_between_move_and_hard_points + indices_between_move_and_hard_points2
sail_obj.vertex_groups[vg_pin_sail_name].add(indices_hard, 1, 'REPLACE')
sail_obj.vertex_groups[vg_up_down_sail_name].add(indices_move_and_hard, 1, 'REPLACE')
# -----------------------------------------------------------
# Add cloth modifier
# -----------------------------------------------------------
bpy.context.scene.frame_set(1)
sail_obj.modifiers.new('Cloth', 'CLOTH')
sail_obj.modifiers["Cloth"].settings.vertex_group_mass = vg_pin_sail_name
if type == 't' or type == 'v' or type == 'd' or type == 'f' or type == 's' or type == 'g':
sail_obj.modifiers["Cloth"].settings.mass = 0.3 #kg
sail_obj.modifiers["Cloth"].settings.tension_stiffness = 15.0
sail_obj.modifiers["Cloth"].settings.compression_stiffness = 15.0
sail_obj.modifiers["Cloth"].settings.shear_stiffness = 0.001
sail_obj.modifiers["Cloth"].settings.bending_stiffness = 2.0
elif type == 'fp' or type == 'fl' or type == 'fs':
sail_obj.modifiers["Cloth"].settings.mass = 0.01 #kg
sail_obj.modifiers["Cloth"].settings.tension_stiffness = 15.0
sail_obj.modifiers["Cloth"].settings.compression_stiffness = 15.0
sail_obj.modifiers["Cloth"].settings.shear_stiffness = 10
sail_obj.modifiers["Cloth"].settings.bending_stiffness = 2.0
### Animation
def registerCallbackAndPlayAnimation(func):
bpy.context.scene.frame_set(0)
bpy.app.timers.register(functools.partial(func), first_interval=4) # wait 4 sec
bpy.ops.screen.animation_play()
def addWindToScene():
# Add wind to scene
bpy.ops.object.effector_add(type='WIND')
bpy.data.objects['Wind'].field.strength = 65.0
# Select wind
wind = bpy.context.scene.objects["Wind"] # Get the object
bpy.ops.object.select_all(action='DESELECT') # Deselect all objects
bpy.context.view_layer.objects.active = wind # Make the wind the active object
wind.select_set(True)
# Rotate wind object
bpy.context.active_object.rotation_euler[1] = math.radians(84)
if wind_dir == 'l':
bpy.context.active_object.rotation_euler[2] = math.radians(14)
elif wind_dir == 'r':
bpy.context.active_object.rotation_euler[2] = math.radians(-14)
elif wind_dir == 'c':
bpy.context.active_object.rotation_euler[2] = math.radians(0)
def removeWindFromScene():
for obj in bpy.context.scene.objects:
if fnmatch.fnmatchcase(obj.name, "Wind*"):
Wind_Obj = [bpy.context.scene.objects[obj.name]]
bpy.ops.object.delete({"selected_objects": Wind_Obj})
def saveObjPositionAsShapeKey(obj, name):
# Save current vertices position as shape key
bpy.context.view_layer.objects.active = obj
bpy.ops.object.modifier_apply_as_shapekey(keep_modifier=True, modifier="Cloth")
# Rename Shape key to good name
created_shape_key = obj.data.shape_keys.key_blocks[-1]
created_shape_key.name = name
bpy.ops.object.select_all(action='DESELECT')
def createWindStrengthShapeKey():
bpy.ops.screen.animation_cancel(restore_frame=False)
for obj in bpy.context.scene.objects:
if fnmatch.fnmatchcase(obj.name, sail_name_prefix + '*') or fnmatch.fnmatchcase(obj.name, sail_name_prefix):
saveObjPositionAsShapeKey(obj, 'WindStrength')
removeWindFromScene()
createDeformKeyframesForSails()
registerCallbackAndPlayAnimation(createAnimationByClothShapeKeys)
def createDeformKeyframesForSails():
for obj in bpy.context.scene.objects:
if fnmatch.fnmatchcase(obj.name, sail_name_prefix + '*') or fnmatch.fnmatchcase(obj.name, sail_name_prefix):
obj.data.shape_keys.key_blocks['Deform'].keyframe_insert(data_path='value', frame=0)
deform_max = 0.95 # todo const
obj.data.shape_keys.key_blocks['Deform'].value = deform_max
obj.data.shape_keys.key_blocks['Deform'].keyframe_insert(data_path='value', frame=100)
obj.modifiers["Cloth"].settings.vertex_group_mass = vg_up_down_sail_name
def createAnimationByClothShapeKeys():
bpy.ops.screen.animation_cancel(restore_frame=False)
for obj in bpy.context.scene.objects:
if fnmatch.fnmatchcase(obj.name, sail_name_prefix + '*') or fnmatch.fnmatchcase(obj.name, sail_name_prefix):
# Rename Shape key to good name
new_shape_key_name = 'UpDown'
saveObjPositionAsShapeKey(obj, new_shape_key_name)
# Delete old deform from keyframe
obj.data.shape_keys.key_blocks['Deform'].value = 0
obj.data.shape_keys.key_blocks['Deform'].keyframe_delete(data_path='value', frame=0)
obj.data.shape_keys.key_blocks['Deform'].keyframe_delete(data_path='value', frame=100)
# Create keyframes from new shape key
obj.data.shape_keys.key_blocks[new_shape_key_name].value = 0
obj.data.shape_keys.key_blocks[new_shape_key_name].keyframe_insert(data_path='value', frame=0)
obj.data.shape_keys.key_blocks[new_shape_key_name].value = 100
obj.data.shape_keys.key_blocks[new_shape_key_name].keyframe_insert(data_path='value', frame=100)
# Create keyframes for rope bone
rig = obj.parent
for bone_name, bone in rig.pose.bones.items():
bone.keyframe_insert(data_path="location", frame=0)
arm = bpy.data.objects[rig.name]
bpy.context.view_layer.objects.active = arm
bpy.ops.object.mode_set(mode="POSE")
pb = arm.pose.bones[bone_name]
deform_max = 0.95 # todo const
diff = (pb.tail - pb.head) * deform_max
arm.data.bones[bone_name].select=True
bpy.ops.transform.translate(value=diff,
orient_axis_ortho='X',orient_type='GLOBAL',
orient_matrix=((1,0,0),(0,1,0),(0,0,1)),
orient_matrix_type='GLOBAL',mirror=False,
use_proportional_edit=False,proportional_edit_falloff='SMOOTH',
proportional_size=0.069, use_proportional_connected=False,
use_proportional_projected=False)
bone.keyframe_insert(data_path="location", frame=100)
arm.data.bones[bone_name].select=False
bpy.ops.object.mode_set(mode="OBJECT")
# Remove cloth modificator
bpy.context.view_layer.objects.active = obj
bpy.ops.object.modifier_remove(modifier="Cloth")
bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.frame_set(0)
bpy.context.scene.frame_end = 100
bpy.context.view_layer.update()
print("Sails processing done!")
addWindToScene()
registerCallbackAndPlayAnimation(createWindStrengthShapeKey)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment