Rope Simulation Animation

Hi,

I’m new in this forum and also new to Blender (and also new to python).

I try to animate the motion of a rope I simulated in an external multibody simulation suftware. The rope is divided in hundreds of small cylinder segments. Every semgement has 6 position components (translation: xyz, rotation: space fixed xyz). I have written a blender script, which imports the position data. This data is stored in ascii files.

My problem is, that I don’t know how I can create a smooth rope mesh.

I tested hooks with vertex binding and bones.

  • Hooks moves the vertices I selected exactly with the controlling cylinders with no smooth transistion between the cylinders.
  • Bones use with the automatic weighted envelopes better results, but with bones I don’t know, how I can move and rotate the bones seperately (as given in the ascii-data files).

3ds Max has a skin feature, which also allows to use meshes as bones to control the mesh. This feature would work great for my purpose.

My question is, how I can create a rope mesh following the controlling cylinders smoothly in Blender?

Regards
Zulu

Attachments

rope_test_001.blend (698 KB)

I am not entirely sure I would model rope like this in blender - particularly when there are so many better ways. Let me know what you are actually trying to achieve with your rope and I’ll look at it again. There’s a couple of things below to look at, these took about 2 minutes each to produce:


Cheers. Clock. :evilgrin:

The animation is working now. It is not perfect, but is working.
I created an armature and parented it to the rope mesh. The location and the rotation of every bone is constrained to one of the small cylinder segments. The motion of the small cylinder segments is defined by the data in the ascii files. All this is done by a short python script.

When I finished my animation setup, I will create this rope, shown by the attached pictures. The result should look photorealistic but it don’t have to show every little fibres. The camera will never go as close to the rope, as shown in the first picture with the purple rope. At first I have to figure out, how I could cerate this complex structure in blender. My plan is then, to create from the detailed 3d model displacement and bump maps and to use a simplified mesh for animation.



@Clock
I would like to hear what ideas you have to create the mesh.

Following code works now as I wanted. The mesh has been folded on the inner side of the rope during bending. Including Vertex Groups, which do not overlap and bbones solved this problem. Large parts of the code is from various sources in the internet.

(The rope mesh is not created in the sript. I import a obj-file here containing the test rope mesh.)

import bpy, bmesh
import math
from bpy.props import *


# #####################################
# parameter
# #####################################
MY_SCALE = 0.1
DV_Rope_Diameter = 10.5            # [mm]
DV_Rope_Length = 10.5            # [mm]
DV_Rope_Segments = 10            # [mm]

DV_Bbone_Segments = 25
DV_Bone_Envelope_Distance = 6.3 # dummy
DV_Bone_Head_Radius = 5         # dummy
DV_Bone_Tail_Radius = 5         # dummy
DV_Bone_Envelope_Weight = 1.0    # dummy
DV_Bone_Bbone_In = 1.0            
DV_Bone_Bbone_Out = 1.0            


DV_Name_Armarture = 'Armature' # name of armature, given by blender    
DV_Name_Rope_Mesh = 'Group64191' # name of rope mesh from import file (.obj)    
DV_File_Rope_Mesh = "C:\Temp\Data\Rope_1.obj" # name of rope mesh file 
DV_File_Rope_Data = "C:\Temp\Data\MEA_ROPE_" # ascii data files -> MEA_ROPE_001.tab


 

# #####################################
# Clean the scene
# #####################################
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
bpy.ops.object.select_by_type(type='EMPTY')
bpy.ops.object.delete()
bpy.context.scene.render.engine = 'CYCLES'
#bpy.context.scene.unit_settings.system='METRIC'
#scale = bpy.context.scene.unit_settings.scale_length = 1.0
#bpy.context.space_data.grid_scale = 0.01
bpy.context.scene.cursor_location = (0.0, 0.0, 0.0)




# #####################################
# create cylinders
# #####################################
for counter in range(0, DV_Rope_Segments):
    bpy.ops.mesh.primitive_cylinder_add(radius=(DV_Rope_Diameter*MY_SCALE)/2.0, depth=(DV_Rope_Length*MY_SCALE), view_align=False, enter_editmode=False, location=(0.0, 0.0, 0.0), layers=(False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False))
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.context.active_object.location = ((DV_Rope_Length*MY_SCALE)/2+counter*(DV_Rope_Length*MY_SCALE), 0.0, 0.0)  # accepts tupples,  use Vector if you are going to do calcs.
    bpy.context.active_object.rotation_axis_angle = (math.pi/2.0, 0.0, 1.0, 0.0)    
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.context.object.hide_render = True

obj = bpy.data.objects['Cylinder']
obj.name = "Cylinder.000"




# #####################################
# Create armature
# #####################################
# http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Cookbook/Rigging/Add_armature
bpy.ops.object.armature_add()
ob = bpy.context.scene.objects.active
#ob.name ="give me a good name!"
arm = ob.data
 
bpy.ops.object.mode_set(mode='EDIT')
for i in range(DV_Rope_Segments-1):
    bpy.ops.armature.extrude()
    bpy.ops.transform.translate(value=(1.0, 0.0, 0.0))


for i in range(len(arm.edit_bones)):
    eb = arm.edit_bones[i]
    eb.bbone_segments = DV_Bbone_Segments
    eb.head = ((DV_Rope_Length*MY_SCALE)*i,0,0)
    eb.tail = ((DV_Rope_Length*MY_SCALE)*(i+1),0,0)
    eb.use_inherit_scale = False
    eb.use_inherit_rotation = False
    eb.envelope_distance = DV_Bone_Envelope_Distance*MY_SCALE
    eb.head_radius = DV_Bone_Head_Radius*MY_SCALE
    eb.tail_radius = DV_Bone_Tail_Radius*MY_SCALE
    eb.envelope_weight = DV_Bone_Envelope_Weight
    eb.bbone_in = DV_Bone_Bbone_In
    eb.bbone_out = DV_Bone_Bbone_Out
    



# #####################################
# Bone constraints. Armature must be in pose mode.
# #####################################
for i in range(len(arm.edit_bones)):
    bpy.ops.object.mode_set(mode='POSE')    
    # Copy Location constraints 
    cns = ob.pose.bones[i].constraints.new('COPY_LOCATION')
    cns.name = 'Copy_Location'
    cns.target = bpy.data.objects["Cylinder." + str(i).zfill(3)]
    cns.subtarget = ""
    cns.owner_space = 'WORLD'
    cns.target_space = 'WORLD'
    
    # Copy rotation constraints 
    cns = ob.pose.bones[i].constraints.new('COPY_ROTATION')
    cns.name = 'Copy_Rotation'
    cns.target = bpy.data.objects["Cylinder." + str(i).zfill(3)]
    cns.subtarget = ""
    cns.owner_space = 'WORLD'
    cns.target_space = 'WORLD'
    cns.use_offset = True

bpy.ops.object.mode_set(mode='OBJECT')




# #####################################
# Create cylinder motion from ASCII file
# #####################################
for n in range(0, DV_Rope_Segments):
    filename= DV_File_Rope_Data + "%i.tab" % (n+1) 
    x=0
    for line in open(filename):
        line=line.rstrip("
")
        temp=line.split()
        cylindername = "Cylinder.%03i" % n
        knob = bpy.data.objects[cylindername]
        knob.rotation_mode = 'XYZ'
        knob.location = ( float(temp[1])*MY_SCALE,-float(temp[3])*MY_SCALE,float(temp[2])*MY_SCALE )
        knob.rotation_euler = ( float(temp[4])*math.pi/180.0,
                                float(temp[5])*math.pi/180.0,
                                float(temp[6])*math.pi/180.0 )
                        
        # set the keyframe at frame 
        x=x+1
        knob.keyframe_insert(data_path="location", frame=x)
        knob.keyframe_insert(data_path="rotation_euler", frame=x)




# #####################################
# Import Rope Mesh
# #####################################
bpy.ops.import_scene.obj(filepath=DV_File_Rope_Mesh) 
#bpy.context.object.name = 'Rope'
bpy.data.objects[DV_Name_Rope_Mesh].select = True
bpy.data.objects[DV_Name_Armarture].select = True
bpy.ops.object.parent_set(type='ARMATURE_ENVELOPE')





# #####################################
# Define new Vertex Groups
# #####################################
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[DV_Name_Armarture].select = False
bpy.data.objects[DV_Name_Rope_Mesh].select = True

#ob = bpy.context.active_object
ob = bpy.data.objects[DV_Name_Rope_Mesh]
me = ob.data
  
#Let's get the bmesh data
bm = bmesh.new()
bm.from_mesh(me)

for n in range(0, DV_Rope_Segments):

    if n == 0:
        groupName = "Bone"
    else:
        groupName = "Bone.%03i" % n
    
    # Try to retrieve the vertex group, and if we can't make a new one
    try:
        group_index = ob.vertex_groups[groupName].index
    except:
        group = ob.vertex_groups.new(groupName)
        group_index = group.index

    # Now here's something new... we have to get the custom data layer
    # The vertex groups are all in the deform layer (there is always just one, which we retrieve as the active one)
    deform_layer = bm.verts.layers.deform.active
    if deform_layer is None: deform_layer = bm.verts.layers.deform.new()
     
    #verts = [vert.co for vert in bm.verts]
    #plain_verts = [vert.to_tuple() for vert in verts]

    for vert in bm.verts:
        if ( (vert.co[0] > (DV_Rope_Length*MY_SCALE)*n) and (vert.co[0] < (DV_Rope_Length*MY_SCALE)*(n+1) ) ):
            vert[deform_layer][group_index] = 1.0
        else:
            vert[deform_layer][group_index] = 0.0
        if n == 0: 
            if ( vert.co[0] < 0 ):
                vert[deform_layer][group_index] = 1.0    
        if n == (DV_Rope_Segments-1):
            if ( vert.co[0] > (DV_Rope_Length*MY_SCALE*DV_Rope_Segments) ):
                vert[deform_layer][group_index] = 1.0    

bpy.ops.object.mode_set(mode='OBJECT')

# That's it... we're done... so we put the bmesh back in the object
bm.to_mesh(me)
bm.free()


looks like a great script… was about to show you the Array and Spline Method… but I think your script may very well be the best solution… especially if you have position data already… Thanks for the post!