Created
January 2, 2023 15:56
-
-
Save Dangetsu/3136c6aad1ad42b75d5cb0c1a16b2095 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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